checksums.py python
65 lines 2.1 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 3 days ago
1 """Checksum helpers for release artifacts (§QR.7.3)."""
2
3 from __future__ import annotations
4
5 import hashlib
6 from pathlib import Path
7
8
9 def sha256_file(path: Path, *, chunk_size: int = 1024 * 1024) -> str:
10 """Return lowercase hex SHA-256 of ``path`` using single-pass streaming."""
11 digest = hashlib.sha256()
12 with path.open("rb") as handle:
13 while True:
14 chunk = handle.read(chunk_size)
15 if not chunk:
16 break
17 digest.update(chunk)
18 return digest.hexdigest()
19
20
21 def format_sha256sums_line(sha256: str, filename: str) -> str:
22 """Return one ``SHA256SUMS.txt`` line (``sha256 filename``)."""
23 return f"{sha256.lower()} {filename}"
24
25
26 def write_sha256sums(
27 destination: Path,
28 entries: list[tuple[str, str]],
29 ) -> str:
30 """Write ``SHA256SUMS.txt`` content and return the text written.
31
32 Parameters
33 ----------
34 destination:
35 Output path (typically ``SHA256SUMS.txt``).
36 entries:
37 List of ``(sha256, filename)`` pairs in publish order.
38 """
39 lines = [format_sha256sums_line(sha, name) for sha, name in entries]
40 text = "\n".join(lines) + ("\n" if lines else "")
41 destination.write_text(text, encoding="utf-8")
42 return text
43
44
45 def parse_sha256sums(text: str) -> dict[str, str]:
46 """Parse ``sha256 filename`` lines into ``{filename: sha256}``.
47
48 Filenames may contain spaces; the separator is two spaces after the digest
49 (GNU ``sha256sum`` style), with a fallback to the first whitespace split
50 when no double-space is present.
51 """
52 result: dict[str, str] = {}
53 for line in text.splitlines():
54 stripped = line.strip()
55 if not stripped or stripped.startswith("#"):
56 continue
57 if " " in stripped:
58 sha256, filename = stripped.split(" ", 1)
59 else:
60 parts = stripped.split(None, 1)
61 if len(parts) < 2:
62 raise ValueError(f"invalid SHA256SUMS line: {line!r}")
63 sha256, filename = parts[0], parts[1]
64 result[filename.strip()] = sha256.strip().lower()
65 return result
File History 1 commit
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 3 days ago