Skip to content

Commit e8cf8cf

Browse files
committed
chore(7107): Include a Sentry Explore Link in the Announcement
1 parent 45043af commit e8cf8cf

7 files changed

Lines changed: 457 additions & 23 deletions

File tree

.github/workflows/publish-prerelease.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ jobs:
123123
MERGE_BASE_COMMIT_HASH: '' # placeholder so that we have autocomplete and logs
124124
CLOUDFRONT_REPO_URL: ${{ vars.AWS_CLOUDFRONT_URL }}/${{ github.event.repository.name }}
125125
HOST_URL: ${{ vars.AWS_CLOUDFRONT_URL }}/${{ github.event.repository.name }}/${{ github.run_id }}
126+
SENTRY_DSN_PERFORMANCE: ${{ vars.SENTRY_DSN_PERFORMANCE }}
126127
BENCHMARK_WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id || github.run_id }}
127128
LAVAMOAT_POLICY_CHANGED: ${{ inputs.lavamoat-policy-changed }}
128129
POST_NEW_BUILDS: ${{ inputs.post-new-builds }}

development/metamaskbot-build-announce/compare-benchmarks.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ describe('compare-benchmarks', () => {
4747
it('passes when results are within thresholds', () => {
4848
const benchmarks = [
4949
{
50-
name: 'benchmark-chrome-browserify-userJourneyOnboardingImport',
50+
name: 'benchmark-chrome-webpack-userJourneyOnboardingImport',
5151
data: {
5252
onboardingImportWallet: makeBenchmarkResults({
5353
p75: { importWalletToSocialScreen: 1500 },
@@ -66,7 +66,7 @@ describe('compare-benchmarks', () => {
6666
it('fails when p75 exceeds fail threshold', () => {
6767
const benchmarks = [
6868
{
69-
name: 'benchmark-chrome-browserify-userJourneyOnboardingImport',
69+
name: 'benchmark-chrome-webpack-userJourneyOnboardingImport',
7070
data: {
7171
onboardingImportWallet: makeBenchmarkResults({
7272
p75: { importWalletToSocialScreen: 99999 },
@@ -84,7 +84,7 @@ describe('compare-benchmarks', () => {
8484
it('includes relative metrics when baseline is available', () => {
8585
const benchmarks = [
8686
{
87-
name: 'benchmark-chrome-browserify-userJourneyOnboardingImport',
87+
name: 'benchmark-chrome-webpack-userJourneyOnboardingImport',
8888
data: {
8989
onboardingImportWallet: makeBenchmarkResults({
9090
p75: { importWalletToSocialScreen: 1500 },
@@ -124,7 +124,7 @@ describe('compare-benchmarks', () => {
124124
it('resolves page-load baseline for startup benchmarks', () => {
125125
const benchmarks = [
126126
{
127-
name: 'benchmark-chrome-browserify-startupStandardHome',
127+
name: 'benchmark-chrome-webpack-startupStandardHome',
128128
data: {
129129
startupStandardHome: makeBenchmarkResults({
130130
p75: { uiStartup: 1800 },

development/metamaskbot-build-announce/comparison-utils.test.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -366,12 +366,10 @@ describe('THRESHOLD_REGISTRY', () => {
366366
expect(THRESHOLD_REGISTRY.loadNewAccount).toBeDefined();
367367
expect(THRESHOLD_REGISTRY.confirmTx).toBeDefined();
368368
expect(THRESHOLD_REGISTRY.bridgeUserActions).toBeDefined();
369-
expect(
370-
THRESHOLD_REGISTRY['chrome-browserify-loadNewAccount'],
371-
).toBeUndefined();
369+
expect(THRESHOLD_REGISTRY['chrome-webpack-loadNewAccount']).toBeUndefined();
372370
});
373371

374-
it('has platform-agnostic keys for user journey (chrome-browserify + chrome-webpack)', () => {
372+
it('has platform-agnostic keys for user journey (no per-platform threshold keys)', () => {
375373
expect(THRESHOLD_REGISTRY.onboardingImportWallet).toBeDefined();
376374
expect(THRESHOLD_REGISTRY.swap).toBeDefined();
377375
expect(THRESHOLD_REGISTRY['chrome-webpack-swap']).toBeUndefined();
@@ -381,7 +379,7 @@ describe('THRESHOLD_REGISTRY', () => {
381379
expect(THRESHOLD_REGISTRY.startupStandardHome).toBeDefined();
382380
expect(THRESHOLD_REGISTRY.startupPowerUserHome).toBeDefined();
383381
expect(
384-
THRESHOLD_REGISTRY['chrome-browserify-startupStandardHome'],
382+
THRESHOLD_REGISTRY['chrome-webpack-startupStandardHome'],
385383
).toBeUndefined();
386384
expect(
387385
THRESHOLD_REGISTRY['firefox-webpack-startupPowerUserHome'],

development/metamaskbot-build-announce/performance-benchmarks.test.ts

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,125 @@ describe('buildBenchmarkSection', () => {
910910
expect(html).not.toContain('<ul');
911911
});
912912
});
913+
914+
describe('artifact and Sentry links', () => {
915+
const mockDsn = 'https://fake@metamask.sentry.io/4510302346608640';
916+
let savedBranch: string | undefined;
917+
let savedRefName: string | undefined;
918+
let savedSentryDsn: string | undefined;
919+
920+
beforeEach(() => {
921+
savedBranch = process.env.BRANCH;
922+
savedRefName = process.env.GITHUB_REF_NAME;
923+
savedSentryDsn = process.env.SENTRY_DSN_PERFORMANCE;
924+
});
925+
926+
afterEach(() => {
927+
if (savedBranch === undefined) {
928+
delete process.env.BRANCH;
929+
} else {
930+
process.env.BRANCH = savedBranch;
931+
}
932+
if (savedRefName === undefined) {
933+
delete process.env.GITHUB_REF_NAME;
934+
} else {
935+
process.env.GITHUB_REF_NAME = savedRefName;
936+
}
937+
if (savedSentryDsn === undefined) {
938+
delete process.env.SENTRY_DSN_PERFORMANCE;
939+
} else {
940+
process.env.SENTRY_DSN_PERFORMANCE = savedSentryDsn;
941+
}
942+
});
943+
944+
it('includes [Sentry log] when BRANCH and SENTRY_DSN_PERFORMANCE are set', () => {
945+
process.env.BRANCH = 'feat/test';
946+
process.env.SENTRY_DSN_PERFORMANCE = mockDsn;
947+
const html = buildBenchmarkSection(
948+
withEntries([
949+
makeEntry({ artifactUrl: 'https://cdn.example.com/benchmark.json' }),
950+
]),
951+
'Test',
952+
BASELINE_PASS,
953+
);
954+
expect(html).toContain('[Show logs]');
955+
expect(html).toContain('[Sentry log]');
956+
expect(html).toContain('metamask.sentry.io/explore/logs');
957+
});
958+
959+
it('uses GITHUB_REF_NAME for Sentry when BRANCH is unset', () => {
960+
delete process.env.BRANCH;
961+
process.env.GITHUB_REF_NAME = 'release/1.0.0';
962+
process.env.SENTRY_DSN_PERFORMANCE = mockDsn;
963+
const html = buildBenchmarkSection(
964+
withEntries([
965+
makeEntry({ artifactUrl: 'https://cdn.example.com/benchmark.json' }),
966+
]),
967+
'Test',
968+
BASELINE_PASS,
969+
);
970+
expect(html).toContain('[Sentry log]');
971+
});
972+
973+
it('omits [Sentry log] when SENTRY_DSN_PERFORMANCE is unset', () => {
974+
process.env.BRANCH = 'feat/test';
975+
delete process.env.SENTRY_DSN_PERFORMANCE;
976+
const html = buildBenchmarkSection(
977+
withEntries([
978+
makeEntry({ artifactUrl: 'https://cdn.example.com/benchmark.json' }),
979+
]),
980+
'Test',
981+
BASELINE_PASS,
982+
);
983+
expect(html).toContain('[Show logs]');
984+
expect(html).not.toContain('[Sentry log]');
985+
});
986+
987+
it('omits [Sentry log] when branch env vars are unset', () => {
988+
delete process.env.BRANCH;
989+
delete process.env.GITHUB_REF_NAME;
990+
process.env.SENTRY_DSN_PERFORMANCE = mockDsn;
991+
const html = buildBenchmarkSection(
992+
withEntries([
993+
makeEntry({ artifactUrl: 'https://cdn.example.com/benchmark.json' }),
994+
]),
995+
'Test',
996+
BASELINE_PASS,
997+
);
998+
expect(html).toContain('[Show logs]');
999+
expect(html).not.toContain('[Sentry log]');
1000+
});
1001+
1002+
it('includes Sentry links in timer details when env and DSN are set', () => {
1003+
process.env.BRANCH = 'main';
1004+
process.env.SENTRY_DSN_PERFORMANCE = mockDsn;
1005+
const entry = makeEntry({
1006+
benchmarkName: 'swap',
1007+
mean: {
1008+
openSwapPageFromHome: 310,
1009+
fetchAndDisplaySwapQuotes: 5000,
1010+
},
1011+
p75: {
1012+
openSwapPageFromHome: 340,
1013+
fetchAndDisplaySwapQuotes: 4500,
1014+
},
1015+
p95: {
1016+
openSwapPageFromHome: 400,
1017+
fetchAndDisplaySwapQuotes: 5500,
1018+
},
1019+
artifactUrl: 'https://cdn.example.com/swap.json',
1020+
});
1021+
const html = buildBenchmarkSection(
1022+
withEntries([entry]),
1023+
'Test',
1024+
undefined,
1025+
'https://github.com/actions/runs/999',
1026+
);
1027+
expect(html).toContain('<code>fetchAndDisplaySwapQuotes</code>');
1028+
expect(html).toContain('[Show logs]');
1029+
expect(html).toContain('[Sentry log]');
1030+
});
1031+
});
9131032
});
9141033

9151034
describe('buildPerformanceBenchmarksSection', () => {
@@ -1126,6 +1245,36 @@ describe('buildPerformanceBenchmarksSection', () => {
11261245
expect(html).toContain('startupStandardHome');
11271246
});
11281247

1248+
it('includes [Sentry log] in matrix and regression list when BRANCH and SENTRY_DSN_PERFORMANCE are set', async () => {
1249+
const mockDsn = 'https://fake@metamask.sentry.io/4510302346608640';
1250+
const savedBranch = process.env.BRANCH;
1251+
const savedSentry = process.env.SENTRY_DSN_PERFORMANCE;
1252+
try {
1253+
process.env.BRANCH = 'main';
1254+
process.env.SENTRY_DSN_PERFORMANCE = mockDsn;
1255+
mockFetch.mockResolvedValue({
1256+
ok: true,
1257+
json: () => Promise.resolve(FAILING_PAYLOAD),
1258+
} as unknown as Response);
1259+
1260+
const html = await buildPerformanceBenchmarksSection(HOST);
1261+
1262+
expect(html).toContain('[Sentry log]');
1263+
expect(html).toContain('metamask.sentry.io/explore/logs');
1264+
} finally {
1265+
if (savedBranch === undefined) {
1266+
delete process.env.BRANCH;
1267+
} else {
1268+
process.env.BRANCH = savedBranch;
1269+
}
1270+
if (savedSentry === undefined) {
1271+
delete process.env.SENTRY_DSN_PERFORMANCE;
1272+
} else {
1273+
process.env.SENTRY_DSN_PERFORMANCE = savedSentry;
1274+
}
1275+
}
1276+
});
1277+
11291278
it('exercises getEntryRegressions with baseline data when entry is non-pass', async () => {
11301279
mockFetch.mockResolvedValue({
11311280
ok: true,

development/metamaskbot-build-announce/performance-benchmarks.ts

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
buildArtifactFilename,
4747
buildArtifactUrl,
4848
} from './utils';
49+
import { buildSentryLogsUrl } from './sentry-utils';
4950

5051
export type BenchmarkEntry = {
5152
benchmarkName: string;
@@ -476,6 +477,53 @@ function extractDisplayName(benchmarkName: string): string {
476477
return match ? match[1] : benchmarkName;
477478
}
478479

480+
/**
481+
* Builds HTML for an artifact log link and optional Sentry Logs Explorer link.
482+
*
483+
* @param logHref - Optional artifact log URL
484+
* @param entry - Benchmark entry (for Sentry filters)
485+
* @param logLinkInnerHtml - Inner HTML for the artifact log anchor (e.g. '[Show logs]')
486+
*/
487+
function buildArtifactAndSentryLinksHtml(
488+
logHref: string | undefined,
489+
entry: BenchmarkEntry | undefined,
490+
logLinkInnerHtml: string,
491+
): string {
492+
const branchName = process.env.BRANCH || process.env.GITHUB_REF_NAME;
493+
const sentryUrl =
494+
branchName && entry
495+
? buildSentryLogsUrl(branchName, undefined, {
496+
browser: entry.platform,
497+
buildType: entry.buildType,
498+
benchmarkName: entry.benchmarkName,
499+
})
500+
: null;
501+
502+
const logsLink = logHref
503+
? `<a href="${logHref}">${logLinkInnerHtml}</a>`
504+
: '';
505+
const sentryLinkHtml = sentryUrl
506+
? `<a href="${sentryUrl}">[Sentry log]</a>`
507+
: '';
508+
509+
return [logsLink, sentryLinkHtml].filter(Boolean).join(' ');
510+
}
511+
512+
/**
513+
* Builds table cell content with health icon and links (CI artifact + Sentry).
514+
* @param icon
515+
* @param logHref
516+
* @param entry
517+
*/
518+
function buildCellContent(
519+
icon: string,
520+
logHref: string | undefined,
521+
entry: BenchmarkEntry | undefined,
522+
): string {
523+
const links = buildArtifactAndSentryLinksHtml(logHref, entry, '[Show logs]');
524+
return links ? `${icon} ${links}` : icon;
525+
}
526+
479527
/**
480528
* Counts the number of failing and warning benchmark entries.
481529
*
@@ -561,9 +609,12 @@ function formatTimerDetails(
561609
return null;
562610
}
563611

564-
const logsLine = logHref
565-
? `<br><a href="${logHref}">[Show logs]</a>`
566-
: '';
612+
const linksHtml = buildArtifactAndSentryLinksHtml(
613+
logHref,
614+
entry,
615+
'[Show logs]',
616+
);
617+
const logsLine = linksHtml ? `<br>${linksHtml}` : '';
567618
return `<div>${icon} <code>${metricName}</code>${logsLine}</div>`;
568619
})
569620
.filter((item) => item !== null)
@@ -813,17 +864,20 @@ export function buildBenchmarkSection(
813864
logHref,
814865
);
815866

816-
const logsLink = logHref
817-
? `<a href="${logHref}">[Show logs]</a>`
818-
: '';
819-
820867
let cell: string;
821868
switch (true) {
822-
case Boolean(timerDetails):
823-
cell = timerDetails;
869+
case Boolean(timerDetails): {
870+
const links = buildArtifactAndSentryLinksHtml(
871+
logHref,
872+
entry,
873+
'[Show logs]',
874+
);
875+
const linkLine = links ? `${icon} ${links}` : icon;
876+
cell = `${linkLine}<br>${timerDetails}`;
824877
break;
878+
}
825879
case Boolean(logHref):
826-
cell = `${icon} ${logsLink}`;
880+
cell = buildCellContent(icon, logHref, entry);
827881
break;
828882
default:
829883
cell = icon;
@@ -943,9 +997,12 @@ function buildHealthMatrixHtml(
943997
);
944998
const logHref = entry?.artifactUrl;
945999
const label = data.label ? `${icon} ${data.label}` : icon;
946-
const cell = logHref
947-
? `${label} <a href="${logHref}">[logs]</a>`
948-
: label;
1000+
const links = buildArtifactAndSentryLinksHtml(
1001+
logHref,
1002+
entry,
1003+
'[logs]',
1004+
);
1005+
const cell = links ? `${label} ${links}` : label;
9491006
return `<td align="center">${cell}</td>`;
9501007
})
9511008
.join('');
@@ -1030,7 +1087,12 @@ function buildFailingItemsHtml(
10301087
const worstLabel = getWorstViolationLabel(entry, baselineMetrics);
10311088
const labelPart = worstLabel ? ` — ${worstLabel}` : '';
10321089
const logHref = entry.artifactUrl ?? runUrl;
1033-
const logAnchor = logHref ? ` <a href="${logHref}">[Show logs]</a>` : '';
1090+
const links = buildArtifactAndSentryLinksHtml(
1091+
logHref,
1092+
entry,
1093+
'[Show logs]',
1094+
);
1095+
const logAnchor = links ? ` ${links}` : '';
10341096
return [
10351097
`<li><b>${entry.benchmarkName}</b> · ${entry.platform}-${entry.buildType}${labelPart}${logAnchor}</li>`,
10361098
];

0 commit comments

Comments
 (0)