|
34 | 34 | from pathlib import Path |
35 | 35 | from typing import Any, Literal, cast |
36 | 36 |
|
37 | | -from bench_compare import render_release_artifacts |
| 37 | +from bench_compare import HOW_TO_UPDATE_SECTION, render_release_artifacts |
38 | 38 | from performance_artifacts import ArtifactPaths, PerformanceBundle, ensure_distinct_paths, load_bundle, publish_bundle |
39 | 39 | from subprocess_utils import ExecutableNotFoundError, run_git_command, run_git_command_with_input, run_safe_command |
40 | 40 |
|
@@ -374,46 +374,10 @@ def _read_text(path: Path) -> str: |
374 | 374 | return path.read_text(encoding="utf-8") |
375 | 375 |
|
376 | 376 |
|
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 | | - |
412 | 377 | def _normalize_how_to_update(text: str) -> str: |
413 | | - section = _how_to_update_section() |
414 | 378 | 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}" |
417 | 381 |
|
418 | 382 |
|
419 | 383 | def _replace_file(src: Path, dst: Path) -> None: |
@@ -478,6 +442,11 @@ def _snapshot_regular_file(path: Path, *, label: str) -> bytes | None: |
478 | 442 | return path.read_bytes() |
479 | 443 |
|
480 | 444 |
|
| 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 | + |
481 | 450 | def _restore_snapshots(snapshots: tuple[tuple[Path, bytes | None], ...]) -> tuple[BaseException, ...]: |
482 | 451 | """Attempt every file restoration and return all rollback failures.""" |
483 | 452 | errors: list[BaseException] = [] |
@@ -1431,7 +1400,7 @@ def _existing_archive_matches(*, archive_path: Path | None, current_id: ReportId |
1431 | 1400 | if archived_payload is None: |
1432 | 1401 | msg = "existing archive snapshot invariant violated" |
1433 | 1402 | 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)) |
1435 | 1404 | try: |
1436 | 1405 | archived_id = parse_report_id(archived_text) |
1437 | 1406 | except (TypeError, ValueError) as exc: |
|
0 commit comments