Skip to content

Commit 8b8537b

Browse files
committed
fix(perf): harden release artifact promotion
- Treat CRLF and LF archive reports as equivalent while preserving stored bytes. - Share comparison selection and report-update guidance across generation paths. - Isolate artifact path resolution and retained-artifact rendering. - Clarify that durable benchmark downloads are GitHub Release assets.
1 parent 600445c commit 8b8537b

6 files changed

Lines changed: 225 additions & 126 deletions

File tree

docs/RELEASING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ clean `target/` until the release report review is complete.
154154

155155
For an explicit measurement repair, run
156156
`just performance-release <current-tag> <previous-tag>`. To compare the stored
157-
GitHub Actions release assets instead of running Cargo locally, use
157+
GitHub Release assets instead of running Cargo locally, use
158158
`just performance-github-assets`. The local release workflow validates and then
159159
compiles both library revisions with the current checkout's hashed benchmark
160160
harness, recording source-state, environment, toolchain, dependency, Criterion,

scripts/archive_performance.py

Lines changed: 9 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from pathlib import Path
3535
from typing import Any, Literal, cast
3636

37-
from bench_compare import render_release_artifacts
37+
from bench_compare import HOW_TO_UPDATE_SECTION, render_release_artifacts
3838
from performance_artifacts import ArtifactPaths, PerformanceBundle, ensure_distinct_paths, load_bundle, publish_bundle
3939
from subprocess_utils import ExecutableNotFoundError, run_git_command, run_git_command_with_input, run_safe_command
4040

@@ -374,46 +374,10 @@ def _read_text(path: Path) -> str:
374374
return path.read_text(encoding="utf-8")
375375

376376

377-
def _how_to_update_section() -> str:
378-
lines = [
379-
"## How to Update",
380-
"",
381-
"Local performance reports are generated in isolated temporary worktrees:",
382-
"",
383-
"```bash",
384-
"# Local development: compare the current tree with the latest release",
385-
"just performance-local",
386-
"",
387-
"# Release PR: update docs/PERFORMANCE.md and archive the previous report",
388-
"just performance-release",
389-
"",
390-
"# Re-render and promote from retained CSV/JSON inputs (no benchmarks)",
391-
"just performance-rerender",
392-
"",
393-
"# GitHub Actions release assets",
394-
"just performance-github-assets",
395-
"",
396-
"# Explicit repair",
397-
"just performance-release <current-tag> <previous-tag>",
398-
"```",
399-
"",
400-
"`just performance-local` writes `target/bench-reports/performance.md`.",
401-
"`just performance-github-assets` writes `target/bench-reports/github-assets-performance.md`.",
402-
"`just performance-release` also retains `performance.csv` and `performance.provenance.json` beside the local report.",
403-
"",
404-
"Older curated release-to-release reports are archived in `docs/archive/performance/`.",
405-
"",
406-
"See `docs/BENCHMARKING.md` for the full comparison workflow.",
407-
"",
408-
]
409-
return "\n".join(lines)
410-
411-
412377
def _normalize_how_to_update(text: str) -> str:
413-
section = _how_to_update_section()
414378
if _HOW_TO_UPDATE_RE.search(text):
415-
return _HOW_TO_UPDATE_RE.sub(section, text)
416-
return f"{text.rstrip()}\n\n{section}"
379+
return _HOW_TO_UPDATE_RE.sub(HOW_TO_UPDATE_SECTION, text)
380+
return f"{text.rstrip()}\n\n{HOW_TO_UPDATE_SECTION}"
417381

418382

419383
def _replace_file(src: Path, dst: Path) -> None:
@@ -478,6 +442,11 @@ def _snapshot_regular_file(path: Path, *, label: str) -> bytes | None:
478442
return path.read_bytes()
479443

480444

445+
def _decode_text_snapshot(payload: bytes) -> str:
446+
"""Decode UTF-8 bytes with the universal-newline behavior of ``Path.read_text``."""
447+
return payload.decode("utf-8").replace("\r\n", "\n").replace("\r", "\n")
448+
449+
481450
def _restore_snapshots(snapshots: tuple[tuple[Path, bytes | None], ...]) -> tuple[BaseException, ...]:
482451
"""Attempt every file restoration and return all rollback failures."""
483452
errors: list[BaseException] = []
@@ -1431,7 +1400,7 @@ def _existing_archive_matches(*, archive_path: Path | None, current_id: ReportId
14311400
if archived_payload is None:
14321401
msg = "existing archive snapshot invariant violated"
14331402
raise AssertionError(msg)
1434-
archived_text = _normalize_how_to_update(archived_payload.decode("utf-8"))
1403+
archived_text = _normalize_how_to_update(_decode_text_snapshot(archived_payload))
14351404
try:
14361405
archived_id = parse_report_id(archived_text)
14371406
except (TypeError, ValueError) as exc:

0 commit comments

Comments
 (0)