Skip to content

fix(ci): run benchmarks when benchmark-owned files change on reused builds - #45460

Open
MajorLift wants to merge 1 commit into
jongsun/ci/pin-benchmarks-requiredfrom
jongsun/ci/benchmark-paths-force-run
Open

fix(ci): run benchmarks when benchmark-owned files change on reused builds#45460
MajorLift wants to merge 1 commit into
jongsun/ci/pin-benchmarks-requiredfrom
jongsun/ci/benchmark-paths-force-run

Conversation

@MajorLift

@MajorLift MajorLift commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Changelog

CHANGELOG entry: null

Description

Stacked on #45352. Both change the same set-needs-benchmarks step; separate PRs would conflict in the same block. Base that PR, not main.

A PR that changes only the benchmark harness never runs the benchmarks.

find-reusable-builds hashes application sources. Benchmark-owned code — the harness, the mocks, the thresholds, the gate allowlist — is not in that hash by construction, so such a PR always matches its base branch, always reuses those builds, and needs-benchmarks resolves false. The application is unchanged; what the gate measures about it is not.

Observed across four PRs open on 2026-08-12:

PR changes builds-from-run run-benchmarks
#45443 test/e2e/benchmarks/mocks/ 31579703000 (a main run) skipped
#45444 gated-metrics.ts + test 31579703000 (main) skipped
#45445 .github/scripts/, .github/workflows/ 31579703000 (main) skipped
#45455 .github/scripts/, .github/workflows/ 31585190838 (its own) ran

#45455 is the control: same file types as #45445, but based on a feature branch, so no reusable build was found and its benchmarks ran. The variable is build reuse, not file type.

From the requirements job of run 31582455153:

Searching branch 'jongsun/test/benchmark-solana-discovery-mocks' for matching build hash...
No match on 'jongsun/test/benchmark-solana-discovery-mocks', trying 'main'...

It matched main and skipped. That head SHA carries build-source-hash and builds-from-run statuses but no benchmarks-required status#45352's pin never fires, because the decision short-circuits before it.

What changed

A benchmark_files filter, ORed into the decision. This mirrors the existing unit_integration_test_files rule, which already carries exactly this reasoning — "changes require running unit/integration even when builds are reused" — for test files.

const benchmarkFilesChanged = process.env.FILTER_BENCHMARK_COUNT !== '0';
const freshBuilds = process.env.BUILDS_FROM_BASE_BRANCH !== 'true';
const required = freshBuilds || benchmarkFilesChanged;

The forced run measures the reused base build with the new apparatus. That is deliberate and is the cheaper option: for a harness-only change it is also the comparison that isolates the change, since holding the application constant is what makes the apparatus difference legible. A fresh build would vary both at once.

The decision still flows into #45352's benchmarks-required pinning, so it stays fixed per commit and a re-run cannot flip it.

Verification

Decision table, logic extracted verbatim from the step, run against the four PRs above plus two controls: bench-decision-check.txt

ok   #45443 benchmark mocks, builds reused        before=false after=true  (benchmark-owned files changed)
ok   #45444 gated-metrics + test, builds reused   before=false after=true  (benchmark-owned files changed)
ok   #45445 workflow + script, builds reused      before=false after=true  (benchmark-owned files changed)
ok   #45455 workflow + script, own build          before=true  after=true  (fresh builds)
ok   CONTROL app-only change, builds reused       before=false after=false (builds from base branch)
ok   CONTROL app-only change, own build           before=true  after=true  (fresh builds)

6/6 match expectation

The first control is the one that matters: an ordinary PR that touches no benchmark paths and reuses base builds still skips. This widens the trigger, it does not remove it.

Both files parse (yaml.safe_load on filter-rules.yml and get-requirements.yml).

What this does not prove: that a forced run produces usable numbers against a reused build. The decision logic is checked here; the end-to-end behaviour is only observable once this lands and a harness-only PR runs. The falsifier is direct — #45443 should stop showing run-benchmarks: skipped.

Related issues

Fixes: #45456

Manual testing steps

  1. This PR does not self-trigger, by design. It changes filter-rules.yml and get-requirements.yml, neither of which is in benchmark_filesget-requirements.yml is touched by many unrelated CI PRs, and listing it would run the full benchmark matrix on all of them. (An earlier revision of this section claimed the opposite; that was wrong.) Verify via step 2 instead.
  2. Re-run test(benchmarks): port the completed Solana discovery mocks into the benchmark suite #45443 after this lands and confirm run-benchmarks is no longer skipped, and that its head SHA gains a benchmarks-required commit status.
  3. Confirm an unrelated app-only PR reusing base builds still skips benchmarks.

Pre-merge author checklist


Note

Medium Risk
Changes CI gate logic that decides when the benchmarks job runs, which can affect merge gating. Scope is small and widens the trigger rather than narrowing it.

Overview
Fixes a CI gap where PRs that only change the benchmark harness reused base builds and skipped benchmarks entirely, so apparatus changes were never measured.

Adds a benchmark_files path filter and ORs it into the needs-benchmarks decision: benchmarks now run on fresh builds or benchmark-owned file changes. App-only PRs that reuse builds still skip. Forced runs intentionally measure the reused base build with the new apparatus.

Reviewed by Cursor Bugbot for commit 1e6ed43. Bugbot is set up for automated code reviews on this repo. Configure here.

The build hash covers application sources, so a PR touching only the harness,
the mocks, the thresholds or the gate allowlist always matches its base branch,
always reuses those builds, and never runs the benchmarks it changes. The app
is unchanged; what the gate measures about it is not.

Adds a `benchmark_files` filter and ORs it into the decision, mirroring the
existing `unit_integration_test_files` rule. Such a run measures the reused
base build with the new apparatus, which isolates the apparatus change and
costs no extra build.
@github-actions

Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamask-ci metamask-ci Bot added team-extension-platform Extension Platform team INVALID-PR-TEMPLATE PR's body doesn't match template labels Aug 12, 2026
@MajorLift
MajorLift deployed to pr-comment August 12, 2026 11:04 — with GitHub Actions Active
@metamask-ci

metamask-ci Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
Builds ready [1e6ed43]
⚡ Performance Benchmarks (Total: 🟢 14 pass · 🟡 8 warn · 🔴 2 fail)

Baseline (latest main): 171ed20 | Date: 7/28/2026 | Pipeline: 31588360194 | Baseline logs

Metricschrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🔴 srpButtonToSrpForm(p95) [CI log]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]

Regressions (🔴 2 failures)

Interaction Benchmarks · Samples: 5
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
🟡 load_new_account
confirmTx
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/lcp: -11%
  • confirmTx/longTaskCount: -33%
  • confirmTx/longTaskTotalDuration: -48%
  • confirmTx/tbt: -54%
  • confirmTx/fcp: -10%
  • bridgeUserActions/bridge_load_page: -16%
  • bridgeUserActions/bridge_load_asset_picker: +35%
  • bridgeUserActions/longTaskCount: -44%
  • bridgeUserActions/longTaskTotalDuration: -59%
  • bridgeUserActions/longTaskMaxDuration: -37%
  • bridgeUserActions/tbt: -91%
  • loadNewAccount/load_new_account: +21%
  • loadNewAccount/total: +21%
  • loadNewAccount/inp: -32%
  • loadNewAccount/lcp: +1130%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/inp: -32%
  • confirmTx/fcp: -44%
  • confirmTx/lcp: +1165%
  • bridgeUserActions/bridge_load_page: +340%
  • bridgeUserActions/bridge_load_asset_picker: +193%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +54%
  • bridgeUserActions/inp: -15%
  • bridgeUserActions/fcp: -44%
  • bridgeUserActions/lcp: +1111%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 loadNewAccount/FCP: p75 1.8s
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
startupPowerUserHome
[Sentry log · main/release]
🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/firstReactRender: -99%
  • startupStandardHome/numNetworkReqs: -14%
  • startupStandardHome/domInteractive: -19%
  • startupStandardHome/firstReactRender: -98%
  • startupStandardHome/numNetworkReqs: -13%
  • startupStandardHome/fcp: -14%
  • startupPowerUserHome/load: +19%
  • startupPowerUserHome/domContentLoaded: +19%
  • startupPowerUserHome/domInteractive: +12%
  • startupPowerUserHome/backgroundConnect: +57%
  • startupPowerUserHome/firstReactRender: -99%
  • startupPowerUserHome/initialActions: +11%
  • startupPowerUserHome/loadScripts: +18%
  • startupPowerUserHome/setupStore: +271%
  • startupPowerUserHome/fcp: +16%
  • startupPowerUserHome/lcp: +24%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 startupPowerUserHome/LCP: p75 3.6s
User Journey Benchmarks · Samples: 5 · mock API 🔴 2
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🔴 [CI log]
🔴 total
🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]
🔴 total
🔴 [CI log]
🔴 total
assetDetails
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
solanaAssetDetails
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
importSrpHome
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
sendTransactions
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
swap
[Sentry log · main/release]
🟡 [CI log]
🟡 total
🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/srpButtonToSrpForm: +11%
  • onboardingImportWallet/doneButtonToHomeScreen: -76%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -42%
  • onboardingImportWallet/longTaskCount: -71%
  • onboardingImportWallet/longTaskTotalDuration: -96%
  • onboardingImportWallet/longTaskMaxDuration: -92%
  • onboardingImportWallet/tbt: -99%
  • onboardingImportWallet/total: -79%
  • onboardingNewWallet/longTaskCount: -38%
  • onboardingNewWallet/longTaskTotalDuration: -53%
  • onboardingNewWallet/longTaskMaxDuration: -38%
  • onboardingNewWallet/tbt: -83%
  • solanaAssetDetails/assetClickToPriceChart: +261%
  • solanaAssetDetails/longTaskCount: -100%
  • solanaAssetDetails/longTaskTotalDuration: -100%
  • solanaAssetDetails/longTaskMaxDuration: -100%
  • solanaAssetDetails/tbt: -100%
  • solanaAssetDetails/total: +261%
  • solanaAssetDetails/inp: +11%
  • solanaAssetDetails/fcp: +20%
  • importSrpHome/loginToHomeScreen: -13%
  • importSrpHome/longTaskCount: -37%
  • importSrpHome/longTaskTotalDuration: -45%
  • importSrpHome/longTaskMaxDuration: -24%
  • importSrpHome/tbt: -45%
  • importSrpHome/inp: -48%
  • importSrpHome/cls: +484%
  • sendTransactions/selectTokenToSendFormLoaded: +250%
  • sendTransactions/reviewTransactionToConfirmationPage: -98%
  • sendTransactions/longTaskCount: -100%
  • sendTransactions/longTaskTotalDuration: -100%
  • sendTransactions/longTaskMaxDuration: -100%
  • sendTransactions/tbt: -100%
  • sendTransactions/total: -91%
  • sendTransactions/inp: -26%
  • sendTransactions/fcp: +14%
  • sendTransactions/lcp: -59%
  • sendTransactions/cls: +171%
  • swap/openSwapPageFromHome: +821%
  • swap/fetchAndDisplaySwapQuotes: +133%
  • swap/longTaskCount: +33%
  • swap/longTaskMaxDuration: +26%
  • swap/tbt: +42%
  • swap/total: +147%
  • swap/lcp: -72%
  • swap/cls: -92%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 sendTransactions/FCP: p75 1.9s
  • 🟡 swap/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 sendTransactions/FCP: p75 1.9s
  • 🔴 swap/INP: p75 768ms
  • 🟡 swap/FCP: p75 2.1s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • dappPageLoad/pageLoadTime: -58%
  • dappPageLoad/firstPaint: -46%
  • dappPageLoad/firstContentfulPaint: -46%
Bundle size diffs
  • background: 105 Bytes (0%)
  • ui: 50 Bytes (0%)
  • common: 0 Bytes (0%)
  • other: 0 Bytes (0%)
  • contentScripts: 0 Bytes (0%)
  • zip: 63 Bytes (0%)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

INVALID-PR-TEMPLATE PR's body doesn't match template size-S team-extension-platform Extension Platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant