File size: 2,877 Bytes
05adc6a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env python3
"""Verify every immutable file declared by the v1 release manifest."""

from __future__ import annotations

import hashlib
import json
from pathlib import Path
import sys


RELEASE_ROOT = Path(__file__).resolve().parent
MANIFEST_PATH = RELEASE_ROOT / "MANIFEST.json"


def sha256(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as handle:
        for chunk in iter(lambda: handle.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def main() -> int:
    manifest = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
    failures: list[str] = []
    checked: list[dict[str, object]] = []
    declared_paths = {
        Path(record["path"]).as_posix() for record in manifest["files"]
    }
    actual_paths = {
        path.relative_to(RELEASE_ROOT).as_posix()
        for path in RELEASE_ROOT.rglob("*")
        if (
            path.is_file()
            and path != MANIFEST_PATH
            and "__pycache__" not in path.relative_to(RELEASE_ROOT).parts
            and path.suffix != ".pyc"
        )
    }
    undeclared = sorted(actual_paths - declared_paths)
    absent = sorted(declared_paths - actual_paths)
    if undeclared:
        failures.extend(f"undeclared package file: {path}" for path in undeclared)
    if absent:
        failures.extend(f"declared package file is absent: {path}" for path in absent)
    for record in manifest["files"]:
        relative = Path(record["path"])
        path = (RELEASE_ROOT / relative).resolve()
        if not path.is_relative_to(RELEASE_ROOT):
            failures.append(f"path escapes release root: {relative}")
            continue
        if not path.is_file():
            failures.append(f"missing: {relative}")
            continue
        if path.is_symlink():
            failures.append(f"release file must not be a symlink: {relative}")
            continue
        actual_size = path.stat().st_size
        actual_hash = sha256(path)
        if actual_size != record["size"]:
            failures.append(
                f"size mismatch {relative}: {actual_size} != {record['size']}"
            )
        if actual_hash != record["sha256"]:
            failures.append(
                f"SHA-256 mismatch {relative}: {actual_hash} != "
                f"{record['sha256']}"
            )
        checked.append(
            {
                "path": str(relative),
                "size": actual_size,
                "sha256": actual_hash,
            }
        )

    result = {
        "schema_version": manifest["schema_version"],
        "status": "pass" if not failures else "fail",
        "checked_file_count": len(checked),
        "failures": failures,
    }
    print(json.dumps(result, indent=2, sort_keys=True))
    return 0 if not failures else 1


if __name__ == "__main__":
    raise SystemExit(main())