Skip to content

Commit 1c93440

Browse files
eordanoclaude
andauthored
ci: clamp the duration accumulator and truncate section bodies structurally
Round-6 review fixes: - test.yml: the serialised duration is clamped too — per-case isfinite keeps each addend and the slowest list finite, but a sum of finite doubles (two 1e308s) still overflows to inf, and round(inf,1) would emit bare Infinity into the JSON. Failed test-case entries with neither fullname nor name fall back to "(unnamed)" so sorted(set(...)) cannot hit a None/str TypeError. - upsert-ci-status.sh: the 20k cap now guards both body paths (env and file), and truncation closes any code fence or <details> the cut severed — an unterminated construct would render the rest of the comment inside it, visually eating the neighbouring sections. Committed via API because repository rules require verified signatures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 29b7e7b commit 1c93440

2 files changed

Lines changed: 28 additions & 9 deletions

File tree

.github/actions/ci-status-comment/upsert-ci-status.sh

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,28 @@ set -euo pipefail
3333
# find it, spawning duplicates).
3434
if [ -n "${SECTION_BODY_FILE:-}" ]; then
3535
SECTION_BODY="$(cat "$SECTION_BODY_FILE")"
36-
# GitHub caps an issue comment at 65536 chars across every section; keep one
37-
# writer from consuming the whole budget and failing an unrelated section's
38-
# PATCH with an opaque 422. Truncation is fine for a status section that
39-
# already links out to the full report.
40-
if [ "${#SECTION_BODY}" -gt 20000 ]; then
41-
echo "::warning::Section body is ${#SECTION_BODY} chars; truncating to 20000."
42-
SECTION_BODY="${SECTION_BODY:0:20000}"$'\n\n'"_…truncated; see the linked run for the full report._"
36+
fi
37+
38+
# GitHub caps an issue comment at 65536 chars across every section; keep one
39+
# writer — whichever path its body arrived by — from consuming the whole budget
40+
# and failing an unrelated section's PATCH with an opaque 422. Truncation is
41+
# fine for a status section that already links out to the full report.
42+
if [ "${#SECTION_BODY}" -gt 20000 ]; then
43+
echo "::warning::Section body is ${#SECTION_BODY} chars; truncating to 20000."
44+
SECTION_BODY="${SECTION_BODY:0:20000}"
45+
# Close constructs the cut may have severed — an unterminated code fence or
46+
# <details> makes GitHub render everything after it in this comment inside
47+
# the open block, visually eating the neighbouring sections.
48+
if [ $(( $(grep -c '^```' <<< "$SECTION_BODY") % 2 )) -ne 0 ]; then
49+
SECTION_BODY="$SECTION_BODY"$'\n''```'
4350
fi
51+
opens=$(grep -oi '<details' <<< "$SECTION_BODY" | wc -l || true)
52+
closes=$(grep -oi '</details' <<< "$SECTION_BODY" | wc -l || true)
53+
while [ "${opens:-0}" -gt "${closes:-0}" ]; do
54+
SECTION_BODY="$SECTION_BODY"$'\n</details>'
55+
closes=$((closes + 1))
56+
done
57+
SECTION_BODY="$SECTION_BODY"$'\n\n'"_…truncated; see the linked run for the full report._"
4458
fi
4559

4660
# Fail fast on a section name outside the fence set — an unknown name would

.github/workflows/test.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -841,14 +841,19 @@ jobs:
841841
if case_result == "Passed":
842842
passed += 1
843843
elif case_result == "Failed":
844-
failed.append(test_case.get("fullname") or test_case.get("name"))
844+
# "(unnamed)" keeps the set homogeneous — one None among
845+
# strings makes sorted() raise and kills the whole file.
846+
failed.append(test_case.get("fullname") or test_case.get("name") or "(unnamed)")
845847
846848
result = {
847849
"hasResults": len(xml_files) > 0,
848850
"total": total,
849851
"passed": passed,
850852
"failed": sorted(set(failed)),
851-
"duration": round(duration, 1),
853+
# The per-case clamp keeps each addend (and the slowest list)
854+
# finite, but a sum of finite doubles can still overflow to inf —
855+
# clamp again at the one point the accumulator is serialised.
856+
"duration": round(duration, 1) if math.isfinite(duration) else 0.0,
852857
"slowest": [
853858
{"name": name, "seconds": round(seconds, 1)}
854859
for seconds, name in sorted(timings, key=lambda t: -t[0])[:10]

0 commit comments

Comments
 (0)