Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/llgo-binary-size.yml
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,9 @@ jobs:
"$pages_dir" \
ci/llgo-size/site
deploy-pages:
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
if: >-
always() && github.ref == 'refs/heads/main' &&
github.event_name != 'pull_request' && needs.binary-size.result == 'success'
needs: binary-size
runs-on: ubuntu-24.04
permissions:
Expand Down
3 changes: 3 additions & 0 deletions ci/llgo-size/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ variants. A PR that changes the committed LLGo version runs the full five-way
matrix and uploads the `llgo-binary-size` artifact for review, but still does
not publish history.

Published history is keyed by the full LLGo commit, so rerunning one commit
updates its existing entry instead of adding another build-round entry.

### First-time repository setup

The first publisher run creates the `pages` branch automatically. Before that
Expand Down
44 changes: 41 additions & 3 deletions ci/llgo-size/publish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,46 @@ for legacy in "$pages_dir"/data/runs/*.json; do
mkdir -p "$legacy_dir"
mv "$legacy" "$legacy_dir/results.json"
done

# Consolidate historical runs under the LLGo commit that produced them. This
# keeps reruns of the same commit as one comparable history entry instead of
# creating a new entry for every Actions run number.
python3 - "$pages_dir/data/runs" <<'PY'
import json
import os
import re
import shutil
import sys

runs_dir = sys.argv[1]
for source in sorted(os.listdir(runs_dir)):
source_dir = os.path.join(runs_dir, source)
result_path = os.path.join(source_dir, "results.json")
if not os.path.isdir(source_dir) or not os.path.isfile(result_path):
continue
try:
with open(result_path, encoding="utf-8") as f:
run = json.load(f).get("run", {})
except (OSError, ValueError):
continue
key = run.get("llgoCommit") or run.get("sourceCommit") or source
if not re.fullmatch(r"[A-Za-z0-9._-]+", str(key)) or str(key) == source:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path-traversal / destructive-op gap: the key comes from results.json content (llgoCommit/sourceCommit) and the only guard is re.fullmatch(r"[A-Za-z0-9._-]+", ...), which matches . and ... A results.json in the persisted pages history whose llgoCommit is ".." yields target_dir = runs_dir/.. = .../data, so shutil.rmtree(target_dir) (below) wipes the entire published history and os.rename then clobbers data. "." similarly targets the runs dir itself.

Under the happy path values are 40-char SHAs (LLGO_COMMIT is validated upstream), so this is defense-in-depth rather than an active exploit — but the sole protection deliberately permits the traversal tokens. Reject bare dots explicitly, e.g. add or str(key) in (".", "..") to this skip condition, and apply the same fix to the run_key validator at line 80 (ideally a shared SHA-shaped pattern like ^[0-9a-f]{7,40}$).

continue
target_dir = os.path.join(runs_dir, str(key))
if os.path.exists(target_dir):
existing_path = os.path.join(target_dir, "results.json")
try:
with open(existing_path, encoding="utf-8") as f:
existing = json.load(f).get("run", {})
except (OSError, ValueError):
existing = {}
if str(run.get("createdAt", "")) >= str(existing.get("createdAt", "")):
shutil.rmtree(target_dir)
else:
shutil.rmtree(source_dir)
continue
os.rename(source_dir, target_dir)
PY
cp "$site_dir/index.html" "$pages_dir/index.html"
cp "$site_dir/app.js" "$pages_dir/app.js"
cp "$site_dir/style.css" "$pages_dir/style.css"
Expand All @@ -36,9 +76,7 @@ import sys

with open(sys.argv[1], encoding="utf-8") as f:
run = json.load(f)["run"]
run_id = str(run.get("id") or "manual")
attempt = run.get("attempt")
key = run_id if not attempt else run_id + "-" + str(attempt)
key = str(run.get("llgoCommit") or run.get("sourceCommit") or run.get("id") or "manual")
if not re.fullmatch(r"[A-Za-z0-9._-]+", key):
raise SystemExit("invalid run key: " + repr(key))
print(key)
Expand Down
21 changes: 11 additions & 10 deletions ci/llgo-size/site/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@ function runNumber(run) {
return run.number == null ? run.key : "#" + run.number;
}

function commitLabel(run) {
return shortSha(run.llgoCommit || run.sourceCommit || run.key);
}

function runLabel(run) {
return runNumber(run) + " · " + dateLabel(run.createdAt);
return commitLabel(run) + " · " + dateLabel(run.createdAt);
}

async function loadRun(meta) {
Expand Down Expand Up @@ -154,8 +158,8 @@ function renderComparison(newer, baseline) {
const baselineByName = baseline ? benchmarkMap(baseline) : new Map();
const benchmarkNames = Array.from(new Set(Array.from(newerByName.keys()).concat(Array.from(baselineByName.keys())))).sort();

const newerHeading = "Newer · " + runNumber(newer.run);
const baselineHeading = baseline ? "Older · " + runNumber(baseline.run) : "Older";
const newerHeading = "Newer · " + commitLabel(newer.run);
const baselineHeading = baseline ? "Older · " + commitLabel(baseline.run) : "Older";
grid.innerHTML = benchmarkNames.map(function (name) {
const newerBenchmark = newerByName.get(name);
const baselineBenchmark = baselineByName.get(name);
Expand All @@ -178,13 +182,10 @@ function renderHistoryTable(runs) {
const body = document.querySelector("#history-body");
body.replaceChildren();
for (const run of runs) {
const link = run.workflowUrl
? '<a href="' + escapeHtml(run.workflowUrl) + '">' + escapeHtml(runNumber(run)) + "</a>"
: escapeHtml(runNumber(run));
const row = document.createElement("tr");
row.innerHTML =
"<td>" + link + "<small>" + escapeHtml(dateLabel(run.createdAt)) + "</small></td>" +
"<td><code>" + escapeHtml(shortSha(run.llgoCommit)) + "</code></td>" +
"<td><code>" + (run.workflowUrl ? '<a href="' + escapeHtml(run.workflowUrl) + '">' : "") + escapeHtml(commitLabel(run)) + (run.workflowUrl ? "</a>" : "") + "</td>" +

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Malformed HTML: unclosed <code>. This cell opens <td><code> but closes only with </td> — the <code> element is never closed (</code> is missing before </td>). Every history row emits unbalanced markup; the browser will auto-recover but the monospace styling can bleed into following cells depending on how the parser reconciles the open element. The pre-PR version closed it (... + "</code></td>"). Fix: append </code> before </td>.

"<td>" + escapeHtml(dateLabel(run.createdAt)) + "</td>" +
"<td>" + escapeHtml(run.goVersion || "—") + "</td>" +
"<td>" + escapeHtml(run.llvmVersion || "—") + "</td>" +
"<td>" + escapeHtml(run.ref || "—") + "</td>";
Expand Down Expand Up @@ -238,11 +239,11 @@ function renderHistoryChart(name, documents) {
const color = seriesColors[configIndex % seriesColors.length];
return '<path d="' + chartPath(points, x, y) + '" fill="none" stroke="' + color + '" stroke-width="2.5"></path>' + points.map(function (point) {
return '<circle cx="' + x(point.index) + '" cy="' + y(point.value) + '" r="3.5" fill="' + color + '"><title>' +
escapeHtml(name + " · " + config + " · " + runNumber(point.document.run) + ": " + formatBytes(point.value)) + "</title></circle>";
escapeHtml(name + " · " + config + " · " + commitLabel(point.document.run) + ": " + formatBytes(point.value)) + "</title></circle>";
}).join("");
}).join("");
const labels = documents.map(function (document, index) {
return '<text class="chart-axis-label" text-anchor="middle" x="' + x(index) + '" y="' + (height - 14) + '">' + escapeHtml(runNumber(document.run)) + "</text>";
return '<text class="chart-axis-label" text-anchor="middle" x="' + x(index) + '" y="' + (height - 14) + '">' + escapeHtml(commitLabel(document.run)) + "</text>";
}).join("");
const legend = configs.map(function (config, index) {
return '<span style="--series:' + seriesColors[index % seriesColors.length] + '">' + escapeHtml(configLabels[config] || config) + "</span>";
Expand Down
6 changes: 3 additions & 3 deletions ci/llgo-size/site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LLGo binary-size history</title>
<link rel="stylesheet" href="style.css?v=20260722-2300">
<link rel="stylesheet" href="style.css?v=20260723-1200">
</head>
<body>
<main class="shell">
Expand Down Expand Up @@ -47,7 +47,7 @@ <h2 id="history-heading">Size history</h2>
<div id="history-charts" class="chart-grid" aria-live="polite"></div>
<div class="table-wrap">
<table>
<thead><tr><th>Run</th><th>LLGo commit</th><th>Go</th><th>LLVM</th><th>Workflow</th></tr></thead>
<thead><tr><th>LLGo commit</th><th>Created</th><th>Go</th><th>LLVM</th><th>Workflow</th></tr></thead>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Header/content mismatch: the last column header is Workflow, but renderHistoryTable (app.js:191) renders run.ref (e.g. refs/heads/main) in that 5th cell — the workflow link was moved into the first (LLGo commit) cell. Rename this header to Ref (or render the workflow link in the last cell) so the column label matches what is displayed.

<tbody id="history-body"></tbody>
</table>
</div>
Expand All @@ -59,6 +59,6 @@ <h2 id="history-heading">Size history</h2>
<a href="data/index.json">Raw run index</a>
</footer>
</main>
<script src="app.js?v=20260722-2300"></script>
<script src="app.js?v=20260723-1200"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions docs/llgo-binary-size-handoff.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
PR 修改 `llgo-version.env`,则运行完整的 LLGo 二进制大小矩阵并上传 artifact
供开发者参考,但两种 PR 都不会发布 Pages。
两个工作流使用独立的并发队列,页面刷新不会淘汰等待中的二进制大小构建。
历史目录和索引使用完整 LLGo commit 作为 key,不再使用 Actions 构建轮次作为
历史标识。

核心文件:

Expand Down
Loading