@@ -2,7 +2,7 @@ name: Benchmark Frontend CI
22
33on :
44 push :
5- branches : [master]
5+ branches : [master, frontend-dashboard-data-pipeline ]
66 paths :
77 - ' .github/workflows/benchmark-frontend.yml'
88 - ' dev/benchmarks/**'
2727
2828jobs :
2929 python-data :
30+ if : github.event_name == 'pull_request' || github.ref == 'refs/heads/master'
3031 runs-on : ubuntu-latest
3132 strategy :
3233 matrix :
4849 run : python dev/benchmarks/generate_benchmark_data.py --check --strict-sources
4950
5051 frontend :
52+ if : github.event_name == 'pull_request' || github.ref == 'refs/heads/master'
5153 runs-on : ubuntu-latest
5254 defaults :
5355 run :
7173 - run : npm run build
7274
7375 staleness :
76+ if : github.event_name == 'pull_request' || github.ref == 'refs/heads/master'
7477 runs-on : ubuntu-latest
7578 steps :
7679 - uses : actions/checkout@v4
@@ -104,6 +107,7 @@ jobs:
104107 echo "OK — generated benchmark assets are current"
105108
106109 frontend-e2e :
110+ if : github.event_name == 'pull_request' || github.ref == 'refs/heads/master'
107111 runs-on : ubuntu-latest
108112 needs : [frontend, staleness]
109113 defaults :
@@ -123,3 +127,248 @@ jobs:
123127 name : playwright-report
124128 path : frontend/playwright-report/
125129 retention-days : 30
130+
131+ refresh-june-dashboard :
132+ if : github.event_name == 'push' && github.ref == 'refs/heads/frontend-dashboard-data-pipeline'
133+ runs-on : ubuntu-latest
134+ permissions :
135+ contents : write
136+ steps :
137+ - uses : actions/checkout@v4
138+ with :
139+ ref : frontend-dashboard-data-pipeline
140+ - uses : actions/setup-python@v5
141+ with :
142+ python-version : ' 3.11'
143+ - uses : actions/setup-node@v4
144+ with :
145+ node-version : ' 20'
146+ - name : Update benchmark index summary
147+ run : |
148+ python - <<'PY'
149+ from pathlib import Path
150+ import re
151+
152+ path = Path('docs/en/guides/benchmarks.md')
153+ text = path.read_text(encoding='utf-8')
154+ replacement = '''## Benchmark Dashboard
155+
156+ - **Interactive dashboard**: [Open Dashboard](../../assets/benchmarks/index.html)
157+ - **Dashboard guide**: [Filters, charts, metrics, and reproduction](statgpu_benchmark_dashboard.md)
158+
159+ The canonical dashboard is restricted to benchmark sources dated **2026-06-01 or later**. Eight sources are currently registered; April 2026 ElasticNet, LassoCV, comprehensive-validation, Cox package-comparison, and knockoff results are not connected.
160+
161+ The current bundle covers robust/quantile, unsupervised, ordered, nonparametric, panel, covariance, survival, GLM, and recent linear-model benchmarks. Linear models use the June 2026 squared-error rows from `penalized_glm_perf_20260622.json` and `glm_solver_20260623.json`.
162+
163+ Current capabilities:
164+
165+ - Environment and multi-category navigation.
166+ - Progressive model, variant, penalty, solver, and scale filters.
167+ - NumPy, CuPy, and Torch backend selection.
168+ - Context-aware external comparisons with scikit-learn, linearmodels, and pyGAM.
169+ - Timing and speedup charts with distinct computed and runner-reported semantics.
170+ - Sortable run-level table.
171+ - Validation, accuracy, inference, prediction, convergence, and selection panels.
172+ - Source provenance, parse-report metadata, and source-inventory coverage.
173+
174+ Generate and validate the canonical bundle:
175+
176+ ```bash
177+ python dev/benchmarks/generate_benchmark_data.py \
178+ --out frontend/public/data/benchmark_data.json \
179+ --report frontend/public/data/parse_report.json \
180+ --inventory-out frontend/public/data/source_inventory.json \
181+ --deterministic --strict-sources
182+
183+ python dev/benchmarks/generate_benchmark_data.py --check --strict-sources
184+ ```
185+
186+ Build the deployed dashboard:
187+
188+ ```bash
189+ cd frontend
190+ npm ci
191+ npm run typecheck
192+ npm run build
193+ ```
194+ '''
195+ replacement = '\n'.join(
196+ line[10:] if line.startswith(' ') else line
197+ for line in replacement.splitlines()
198+ )
199+ text, count = re.subn(
200+ r'## Benchmark Dashboard\n.*?(?=\n---)',
201+ replacement,
202+ text,
203+ count=1,
204+ flags=re.S,
205+ )
206+ if count != 1:
207+ raise RuntimeError('Benchmark Dashboard section not found exactly once')
208+ path.write_text(text, encoding='utf-8')
209+ PY
210+ - name : Validate and generate data
211+ run : |
212+ python -m pip install -U pip pytest jsonschema
213+ pytest \
214+ dev/tests/test_benchmark_frontend_data.py \
215+ dev/tests/test_frontend_contracts.py \
216+ dev/tests/test_frontend_domain_coverage.py -v
217+ python dev/benchmarks/generate_benchmark_data.py \
218+ --out frontend/public/data/benchmark_data.json \
219+ --report frontend/public/data/parse_report.json \
220+ --inventory-out frontend/public/data/source_inventory.json \
221+ --deterministic --strict-sources
222+ - name : Build and test frontend
223+ working-directory : frontend
224+ run : |
225+ npm ci
226+ npm run typecheck
227+ npm run build
228+ npx playwright install --with-deps chromium
229+ npm run test:e2e
230+ - name : Restore permanent workflow and commit generated assets
231+ run : |
232+ rm -f .github/workflows/finalize-june-dashboard.yml
233+ cat > .github/workflows/benchmark-frontend.yml <<'EOF'
234+ name: Benchmark Frontend CI
235+
236+ on:
237+ push:
238+ branches: [master]
239+ paths:
240+ - '.github/workflows/benchmark-frontend.yml'
241+ - 'dev/benchmarks/**'
242+ - 'dev/tests/test_benchmark_frontend_data.py'
243+ - 'dev/tests/test_frontend_contracts.py'
244+ - 'dev/tests/test_frontend_domain_coverage.py'
245+ - 'dev/tests/fixtures/benchmark_frontend/**'
246+ - 'frontend/**'
247+ - 'docs/assets/benchmarks/**'
248+ - 'results/benchmark_frontend_sources/**'
249+ pull_request:
250+ paths:
251+ - '.github/workflows/benchmark-frontend.yml'
252+ - 'dev/benchmarks/**'
253+ - 'dev/tests/test_benchmark_frontend_data.py'
254+ - 'dev/tests/test_frontend_contracts.py'
255+ - 'dev/tests/test_frontend_domain_coverage.py'
256+ - 'dev/tests/fixtures/benchmark_frontend/**'
257+ - 'frontend/**'
258+ - 'docs/assets/benchmarks/**'
259+ - 'results/benchmark_frontend_sources/**'
260+
261+ jobs:
262+ python-data:
263+ runs-on: ubuntu-latest
264+ strategy:
265+ matrix:
266+ python-version: ['3.9', '3.11']
267+ steps:
268+ - uses: actions/checkout@v4
269+ - uses: actions/setup-python@v5
270+ with:
271+ python-version: ${{ matrix.python-version }}
272+ - name: Install test dependencies
273+ run: python -m pip install -U pip pytest jsonschema
274+ - name: Run generator and parser tests
275+ run: |
276+ pytest \
277+ dev/tests/test_benchmark_frontend_data.py \
278+ dev/tests/test_frontend_contracts.py \
279+ dev/tests/test_frontend_domain_coverage.py -v
280+ - name: Validate generator output
281+ run: python dev/benchmarks/generate_benchmark_data.py --check --strict-sources
282+
283+ frontend:
284+ runs-on: ubuntu-latest
285+ defaults:
286+ run:
287+ working-directory: frontend
288+ steps:
289+ - uses: actions/checkout@v4
290+ - uses: actions/setup-node@v4
291+ with:
292+ node-version: '20'
293+ - name: Reject committed frontend artifacts
294+ run: |
295+ if git -C "$GITHUB_WORKSPACE" ls-files \
296+ 'frontend/node_modules/**' \
297+ 'frontend/test-results/**' \
298+ 'frontend/playwright-report/**' | grep -q .; then
299+ echo "Generated frontend dependencies/test artifacts must not be committed."
300+ exit 1
301+ fi
302+ - run: npm ci
303+ - run: npm run typecheck
304+ - run: npm run build
305+
306+ staleness:
307+ runs-on: ubuntu-latest
308+ steps:
309+ - uses: actions/checkout@v4
310+ - uses: actions/setup-python@v5
311+ with:
312+ python-version: '3.11'
313+ - uses: actions/setup-node@v4
314+ with:
315+ node-version: '20'
316+ - name: Generate data (deterministic)
317+ run: |
318+ python dev/benchmarks/generate_benchmark_data.py \
319+ --out frontend/public/data/benchmark_data.json \
320+ --report frontend/public/data/parse_report.json \
321+ --inventory-out frontend/public/data/source_inventory.json \
322+ --deterministic --strict-sources
323+ - name: Build frontend
324+ run: |
325+ cd frontend
326+ npm ci
327+ npm run build
328+ - name: Check staleness
329+ run: |
330+ changes="$(git status --porcelain -- frontend/public/data docs/assets/benchmarks)"
331+ if [ -n "$changes" ]; then
332+ echo "Generated benchmark assets are stale:"
333+ printf '%s\n' "$changes"
334+ git diff -- frontend/public/data docs/assets/benchmarks
335+ exit 1
336+ fi
337+ echo "OK — generated benchmark assets are current"
338+
339+ frontend-e2e:
340+ runs-on: ubuntu-latest
341+ needs: [frontend, staleness]
342+ defaults:
343+ run:
344+ working-directory: frontend
345+ steps:
346+ - uses: actions/checkout@v4
347+ - uses: actions/setup-node@v4
348+ with:
349+ node-version: '20'
350+ - run: npm ci
351+ - run: npx playwright install --with-deps chromium
352+ - run: npm run test:e2e
353+ - uses: actions/upload-artifact@v4
354+ if: always()
355+ with:
356+ name: playwright-report
357+ path: frontend/playwright-report/
358+ retention-days: 30
359+ EOF
360+ sed -i 's/^ //' .github/workflows/benchmark-frontend.yml
361+ read runs models <<EOF
362+ $(python - <<'PY'
363+ import json
364+ from pathlib import Path
365+ data = json.loads(Path('frontend/public/data/benchmark_data.json').read_text())
366+ print(len(data['runs']), len(data['models']))
367+ PY
368+ )
369+ EOF
370+ git config user.name github-actions[bot]
371+ git config user.email 41898282+github-actions[bot]@users.noreply.github.com
372+ git add -A
373+ git commit -m "build(frontend): publish June-only bundle (${runs} runs, ${models} models)"
374+ git push origin HEAD:frontend-dashboard-data-pipeline
0 commit comments