-
Notifications
You must be signed in to change notification settings - Fork 3.3k
158 lines (133 loc) · 6.39 KB
/
frontend_profiling.yml
File metadata and controls
158 lines (133 loc) · 6.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
name: "frontend-profiling"
on:
pull_request:
types: [opened, synchronize, reopened]
concurrency:
group: "frontend-profiling-${{ github.event.pull_request.number }}"
cancel-in-progress: true
permissions: {}
jobs:
benchmark:
permissions:
contents: read
name: "frontend-benchmark"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: "gradio-app/gradio/.github/actions/changes@main"
id: changes
with:
filter: "functional"
token: ${{ secrets.GITHUB_TOKEN }}
- name: Find latest release tag
if: steps.changes.outputs.should_run == 'true'
id: latest_tag
run: echo "tag=$(git tag --list 'gradio@*' --sort=-v:refname | head -n 1)" >> "$GITHUB_OUTPUT"
- name: Checkout latest release
if: steps.changes.outputs.should_run == 'true'
run: git checkout ${{ steps.latest_tag.outputs.tag }}
- name: Install dependencies
if: steps.changes.outputs.should_run == 'true'
uses: "gradio-app/gradio/.github/actions/install-all-deps@main"
with:
python_version: "3.10"
os: "ubuntu-latest"
- name: Install Playwright
if: steps.changes.outputs.should_run == 'true'
run: pnpm exec playwright install chromium
- name: Checkout benchmark spec from PR
if: steps.changes.outputs.should_run == 'true'
run: git checkout ${{ github.event.pull_request.head.sha }} -- js/spa/test/tabs_performance.spec.ts
- name: Run benchmark (base)
if: steps.changes.outputs.should_run == 'true'
run: |
. venv/bin/activate
PERF_RESULTS_FILE=/tmp/bench_base.json pnpm exec playwright test \
--config .config/playwright.config.js \
js/spa/test/tabs_performance.spec.ts
- name: Checkout PR branch
if: steps.changes.outputs.should_run == 'true'
run: git checkout ${{ github.event.pull_request.head.sha }}
- name: Install and build PR
if: steps.changes.outputs.should_run == 'true'
run: |
. venv/bin/activate
uv pip install -e client/python
uv pip install -e ".[oauth,mcp]"
pnpm install --no-frozen-lockfile
pnpm build
- name: Run benchmark (PR)
if: steps.changes.outputs.should_run == 'true'
run: |
. venv/bin/activate
PERF_RESULTS_FILE=/tmp/bench_pr.json pnpm exec playwright test \
--config .config/playwright.config.js \
js/spa/test/tabs_performance.spec.ts
- name: Compare results
if: always() && steps.changes.outputs.should_run == 'true'
run: |
node -e "
const fs = require('fs');
let pr, base;
try {
pr = JSON.parse(fs.readFileSync('/tmp/bench_pr.json', 'utf8'));
base = JSON.parse(fs.readFileSync('/tmp/bench_base.json', 'utf8'));
} catch (e) {
console.warn('Could not read benchmark results:', e.message);
process.exit(0);
}
const metrics = [
{ name: 'DOM Content Loaded', key: 'dom_content_loaded_ms', unit: 'ms', warn: 0.25, fail: 0.50 },
{ name: 'Page Load', key: 'page_load_ms', unit: 'ms', warn: 0.25, fail: 0.50 },
{ name: 'LCP', key: 'lcp_ms', unit: 'ms', warn: 0.25, fail: 0.50 },
{ name: 'Tab Navigation', key: 'tab_nav_ms', unit: 'ms', warn: 0.25, fail: 0.50 },
{ name: 'JS Size', key: 'total_js_kb', unit: 'KB', warn: 0.10, fail: 0.25 },
{ name: 'CSS Size', key: 'total_css_kb', unit: 'KB', warn: 0.10, fail: 0.25 },
];
const pad = (s, n) => String(s).padEnd(n);
const rpad = (s, n) => String(s).padStart(n);
console.log('');
console.log('='.repeat(78));
console.log(' Frontend Performance Benchmark');
console.log(' Base: ${{ steps.latest_tag.outputs.tag }}');
console.log('='.repeat(78));
console.log('');
console.log(pad('Metric', 22) + rpad('Base', 12) + rpad('PR', 12) + rpad('Change', 12) + ' Status');
console.log('-'.repeat(78));
let shouldFail = false;
let md = '## Frontend Performance Benchmark\n\n';
md += '| Metric | Base | PR | Change | Status |\n';
md += '|--------|------|----|--------|--------|\n';
for (const m of metrics) {
const bv = base[m.key];
const pv = pr[m.key];
if (bv === undefined || pv === undefined) continue;
const pct = bv === 0 ? 0 : (pv - bv) / bv;
const pctStr = (pct * 100).toFixed(1) + '%';
const changeStr = (pct > 0 ? '+' : '') + pctStr;
const absDiff = pv - bv;
const minAbsDiff = m.unit === 'ms' ? 200 : 0;
let status = 'OK';
let mdStatus = '✅';
if (pct > m.fail && absDiff > minAbsDiff) { status = 'FAIL'; mdStatus = '❌ FAIL'; shouldFail = true; }
else if (pct > m.warn && absDiff > minAbsDiff) { status = 'WARNING'; mdStatus = '⚠️ WARNING'; }
console.log(pad(m.name, 22) + rpad(bv + ' ' + m.unit, 12) + rpad(pv + ' ' + m.unit, 12) + rpad(changeStr, 12) + ' ' + status);
md += '| ' + m.name + ' | ' + bv + ' ' + m.unit + ' | ' + pv + ' ' + m.unit + ' | ' + changeStr + ' | ' + mdStatus + ' |\n';
}
console.log('-'.repeat(78));
console.log(pad('JS Resources', 22) + rpad(base.js_resource_count || '-', 12) + rpad(pr.js_resource_count || '-', 12));
console.log(pad('CSS Resources', 22) + rpad(base.css_resource_count || '-', 12) + rpad(pr.css_resource_count || '-', 12));
console.log('');
md += '\n| JS Resources | ' + (base.js_resource_count || '-') + ' | ' + (pr.js_resource_count || '-') + ' | | |\n';
md += '| CSS Resources | ' + (base.css_resource_count || '-') + ' | ' + (pr.css_resource_count || '-') + ' | | |\n';
md += '\n> Base: \`${{ steps.latest_tag.outputs.tag }}\` | Thresholds: timing warns at +25%/fails at +50%, size warns at +10%/fails at +25%.\n';
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, md);
if (shouldFail) {
console.log('❌ Frontend performance regression detected.');
process.exit(1);
} else {
console.log('✅ No performance regressions detected.');
}
"