Improve LLGo binary-size dashboard#6
Conversation
There was a problem hiding this comment.
FennoAI review — PR #6 · Improve LLGo binary-size dashboard
This PR reworks the client-side dashboard (app.js), the CI workflows, and publish.sh. Overall the change is solid: the security review found no XSS or injection issues (all untrusted JSON fields go through escapeHtml; the repository_dispatch handler validates the source repo and 40-hex commit SHA and passes values via env: rather than ${{ }} interpolation; publish.sh sanitizes the run key against a strict regex before using it as a path component). Findings below; the inline comments carry the highest-confidence items.
Highest priority
app.js:309— likelyTypeErrorwhen the baseline run is null (see inline).renderInspectorguards only!newer, then readsbaseline.rundirectly;loadRunreturnsnullfor an unresolved meta, so a stale/empty A-side selection throws before the|| {}fallback applies.
Documentation (handoff doc only — README is accurate)
docs/llgo-binary-size-handoff.md:25(outside the diff hunk, so noted here): the doc still saysconcurrency.group: llgo-binary-size-pages, but the workflow now usesllgo-binary-size-build(.github/workflows/llgo-binary-size.yml:40) with a separate Pages queue. This also contradicts the newly-added lines 43–45 in the same file. Consider bumping the更新时间(line 3) and updating step 1's "启动一次构建和 Pages 发布" wording —update-pintriggers aworkflow_dispatchbuild; Pages publish is a conditional later step, not directly started by the dispatch.- The stale run-directory keying (
<run>-<attempt>) is flagged inline at line 35.
publish.sh (maintainability / robustness — non-blocking)
- Consolidation runs on every publish: the
python3block re-scans and re-parses every historical run directory on each publish, effectively a one-time migration executed indefinitely. Consider gating it to run only when a legacy (non-commit-keyed) layout is detected. - Lexicographic
createdAttie-break (publish.sh:~59): when two dirs map to the same commit key, "newer wins" usesstr(createdAt) >= str(...). This is only correct for consistently-formatted zero-padded ISO-8601 timestamps; an empty or differently-formattedcreatedAtsilently breaks the intent. A short comment asserting the format (or parsing to a datetime) would harden it.
CI (defense-in-depth — informational)
llgo-binary-size.yml:129-136: thebinary-sizejob declares job-levelpermissions: contents: write, but the write is only needed by themain-gated publish step. The build/clone/CMake steps execute untrusted upstream LLGo/Bent code while a write-capable token is in the environment. Splitting publication into its own job (as done forupdate-pin) would remove the write token during untrusted-build execution. On fork PRs the token is read-only, so this affects same-repo/dispatch runs.
Performance (minor)
- The redundant
benchmarkMaprebuild is flagged inline atapp.js:262. Other minor items (no debounce on the filterinputhandler; fullrenderHistoryre-render on A/B-only changes;cache: "no-store"on immutable per-run JSON) are low-impact at current history sizes.
No blocking issues. Non-approving review (COMMENT).
Findings without inline locations
docs/llgo-binary-size-handoff.md:35: Stale run-directory keying. This still says results are archived topages/data/runs/<run>-<attempt>/, butpublish.sh:79now keys the directory by the full LLGo commit (run.llgoCommit || sourceCommit || id || "manual"). This contradicts the newly-added lines 44–45 of this same file. Suggest updating topages/data/runs/<llgoCommit>/.ci/llgo-size/llgo-version.env:6: Missing trailing newline (\ No newline at end of filein the diff). Harmless forsource, but a POSIX text-file smell and a risk for thesed/awkedits theupdate-pinjob performs on this file. Suggest adding the trailing newline.
| const comparison = percentDelta(newerValue, baselineValue); | ||
| inspector.innerHTML = '<p class="eyebrow">Selected series</p><h3>' + escapeHtml(name) + " · " + escapeHtml(compactConfigLabels[config]) + '</h3><strong class="inspector-value">' + formatBytes(newerValue) + "</strong>" + | ||
| '<p class="inspector-delta ' + deltaClass(relative) + '">' + (config === "Go" ? "Go reference" : formatPercent(relative, 1) + " vs Go") + "</p>" + | ||
| '<dl><div><dt>A → B</dt><dd class="' + deltaClass(comparison) + '">' + formatPercent(comparison, 1) + "</dd></div><div><dt>Baseline</dt><dd><code>" + escapeHtml(commitLabel(baseline.run || {})) + "</code></dd></div><div><dt>Selected</dt><dd><code>" + escapeHtml(commitLabel(newer.run || {})) + "</code></dd></div></dl>"; |
There was a problem hiding this comment.
Likely TypeError when the baseline run is null. renderInspector guards only !newer (line 298), then here reads baseline.run directly. baseline is loadRun(selected.baselineMeta), and loadRun returns null when its meta is falsy (line 108). If baselineSelect.value doesn't resolve to a meta (e.g. a stale value after the run list is filtered, or an empty A-side), baseline is null and baseline.run throws before the || {} fallback can apply — the || {} only protects a missing .run property, not a null baseline.
Suggest guarding explicitly, e.g. commitLabel((baseline && baseline.run) || {}) (and likewise (newer && newer.run) || {}).
| function metricValue(documents, benchmarkName, config, metric) { | ||
| let previous = null; | ||
| return documents.map(function (document, index) { | ||
| const benchmark = benchmarkMap(document).get(benchmarkName); |
There was a problem hiding this comment.
Two small things on this line:
- Redundant
benchmarkMaprebuild (performance).renderHistorycallsmetricValueonce per active benchmark × active config, and each call rebuildsbenchmarkMap(document)for every document. That'sbenchmarks × configs × documentsMap constructions per history render instead of one per document. Consider buildingdocuments.map(benchmarkMap)once inrenderHistoryand passing the pre-built maps in. - Parameter named
documentshadows the globaldocument(also inrenderHistory/renderComparison/benchmarkMap). It works today only because these closures don't touch the DOM; a future edit needingdocument.querySelectorhere would silently operate on a run object. Consider renaming todoc/runDoc.
Summary
Validation