File size: 7,625 Bytes
7ea15a2
6337ffb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7ea15a2
6337ffb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7ea15a2
 
 
 
6337ffb
2527a7b
 
 
 
 
 
 
 
 
6337ffb
2527a7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7ea15a2
6337ffb
 
 
 
 
 
 
 
 
7ea15a2
 
6337ffb
 
0d41924
6337ffb
 
8fcd568
6360508
 
7fa9655
6615497
7fa9655
6337ffb
f407578
74a730f
6615497
50f05ff
2527a7b
9633e56
 
f023c9a
f407578
d7cbfbf
dc5e1e2
20b7585
691f501
8762628
c7dc05e
589cd1c
0568707
 
b6b72a4
 
 
 
 
 
6337ffb
 
 
 
7ea15a2
 
 
 
 
 
6337ffb
 
 
 
 
 
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""Build a clean Lumi Eggcracker zipapp, source archive, and checksums."""

from __future__ import annotations

import argparse
import hashlib
import re
import shutil
import subprocess
import tempfile
import zipapp
import zipfile
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]


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


def version() -> str:
    value = (ROOT / "src" / "lumi_eggcracker" / "__init__.py").read_text(encoding="utf-8")
    match = re.search(r'__version__ = "([0-9]+\.[0-9]+\.[0-9]+)"', value)
    if not match:
        raise RuntimeError("cannot determine public version")
    return match.group(1)


def command(argv: list[str]) -> str:
    result = subprocess.run(argv, capture_output=True, text=True, check=False)
    if result.returncode:
        raise RuntimeError(result.stderr.strip() or result.stdout.strip() or "command failed")
    return result.stdout.strip()


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--output", required=True, type=Path)
    parser.add_argument("--allow-dirty", action="store_true", help=argparse.SUPPRESS)
    args = parser.parse_args()
    if not args.allow_dirty and command(["git", "-C", str(ROOT), "status", "--porcelain"]):
        raise SystemExit("refusing to build a release from a dirty tree")
    release_version = version()
    commit = command(["git", "-C", str(ROOT), "rev-parse", "HEAD"])
    args.output.mkdir(parents=True, exist_ok=True)
    artifact = args.output / f"lumi-eggcracker-{release_version}.pyz"
    source = args.output / f"lumi-eggcracker-{release_version}-source.zip"
    bundle = args.output / f"lumi-eggcracker-{release_version}-linux.zip"
    with tempfile.TemporaryDirectory(prefix="lumi-eggcracker-build-") as raw:
        stage = Path(raw) / "src"
        shutil.copytree(
            ROOT / "src", stage, ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "*.egg-info")
        )
        (stage / "__main__.py").write_text(
            "from lumi_eggcracker.cli import main\nraise SystemExit(main())\n", encoding="utf-8"
        )
        (stage / "lumi_eggcracker" / "build_info.py").write_text(
            f'SOURCE_COMMIT = "{commit}"\n', encoding="utf-8"
        )
        zipapp.create_archive(stage, target=artifact, interpreter="/usr/bin/env python3")
    command(
        [
            "git",
            "-C",
            str(ROOT),
            "archive",
            "--format=zip",
            f"--prefix=lumi-eggcracker-{release_version}/",
            "-o",
            str(source),
            "HEAD",
        ]
    )
    manifest = {
        "artifact": artifact.name,
        "sha256": digest(artifact),
        "source_archive": source.name,
        "source_archive_sha256": digest(source),
        "source_commit": commit,
        "version": release_version,
    }
    (args.output / "release-manifest.json").write_text(
        __import__("json").dumps(manifest, sort_keys=True) + "\n", encoding="utf-8"
    )
    (args.output / "SHA256SUMS").write_text(
        f"{manifest['sha256']}  {artifact.name}\n{manifest['source_archive_sha256']}  {source.name}\n",
        encoding="utf-8",
    )
    prefix = f"lumi-eggcracker-{release_version}"
    release_files = {
        artifact: artifact.name,
        source: source.name,
        args.output / "release-manifest.json": "release-manifest.json",
        args.output / "SHA256SUMS": "SHA256SUMS",
        ROOT / "README.md": "README.md",
        ROOT / "LICENSE": "LICENSE",
        ROOT / "LIMITATIONS.md": "LIMITATIONS.md",
        ROOT / "SECURITY.md": "SECURITY.md",
        ROOT / "SECURITY_MODEL.md": "SECURITY_MODEL.md",
        ROOT / "QUALIFICATION.md": "QUALIFICATION.md",
        ROOT / "RELEASE_NOTES.md": "RELEASE_NOTES.md",
        ROOT / "scripts" / "install.py": "scripts/install.py",
        ROOT / "scripts" / "upgrade.py": "scripts/upgrade.py",
        ROOT / "scripts" / "uninstall.py": "scripts/uninstall.py",
        ROOT / "scripts" / "verify_uninstalled.py": "scripts/verify_uninstalled.py",
        ROOT / "scripts" / "verify_release.py": "scripts/verify_release.py",
        ROOT / "scripts" / "first_kill.py": "scripts/first_kill.py",
        ROOT / "scripts" / "support_bundle.py": "scripts/support_bundle.py",
        ROOT / "scripts" / "prepare_ai_smoke.py": "scripts/prepare_ai_smoke.py",
        ROOT / "scripts" / "prepare_safetensors_smoke.py": "scripts/prepare_safetensors_smoke.py",
        ROOT / "scripts" / "ai_smoke_worker.py": "scripts/ai_smoke_worker.py",
        ROOT / "scripts" / "smoke_local_ai.py": "scripts/smoke_local_ai.py",
        ROOT / "scripts" / "smoke_autonomous_ai.py": "scripts/smoke_autonomous_ai.py",
        ROOT / "scripts" / "smoke_content_ai.py": "scripts/smoke_content_ai.py",
        ROOT / "scripts" / "smoke_safetensors_ai.py": "scripts/smoke_safetensors_ai.py",
        ROOT / "scripts" / "smoke_safetensors_aten_negative.py": "scripts/smoke_safetensors_aten_negative.py",
        ROOT / "scripts" / "run_content_matrix.py": "scripts/run_content_matrix.py",
        ROOT / "scripts" / "run_content_benign_matrix.py": "scripts/run_content_benign_matrix.py",
        ROOT / "scripts" / "run_content_adversarial_matrix.py": "scripts/run_content_adversarial_matrix.py",
        ROOT / "scripts" / "self_validate.py": "scripts/self_validate.py",
        ROOT / "scripts" / "run_autonomous_matrix.py": "scripts/run_autonomous_matrix.py",
        ROOT / "scripts" / "run_native_matrix.py": "scripts/run_native_matrix.py",
        ROOT / "scripts" / "run_p0_native.py": "scripts/run_p0_native.py",
        ROOT / "scripts" / "run_installer_p0.py": "scripts/run_installer_p0.py",
        ROOT / "scripts" / "benchmark_overhead.py": "scripts/benchmark_overhead.py",
        ROOT / "scripts" / "qualify_offline_boundary.py": "scripts/qualify_offline_boundary.py",
        ROOT / "scripts" / "qualify_offline_boundary_integration.py": "scripts/qualify_offline_boundary_integration.py",
        ROOT / "scripts" / "verify_evidence.py": "scripts/verify_evidence.py",
        ROOT / "scripts" / "package_evidence.py": "scripts/package_evidence.py",
        ROOT / "scripts" / "verify_evidence_archive.py": "scripts/verify_evidence_archive.py",
        ROOT / "tests" / "fixtures" / "benign_model_handler.py": "tests/fixtures/benign_model_handler.py",
        ROOT / "tests" / "fixtures" / "benign_near_limit.py": "tests/fixtures/benign_near_limit.py",
        ROOT / "tests" / "fixtures" / "canary.py": "tests/fixtures/canary.py",
        ROOT / "tests" / "fixtures" / "fork_race.py": "tests/fixtures/fork_race.py",
        ROOT / "tests" / "fixtures" / "hostile_client.py": "tests/fixtures/hostile_client.py",
        ROOT / "tests" / "fixtures" / "pid_pressure.py": "tests/fixtures/pid_pressure.py",
    }
    with zipfile.ZipFile(bundle, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
        for path, name in release_files.items():
            archive.write(path, f"{prefix}/{name}")
    # The bundle cannot contain a checksum of itself without recursion.  The
    # detached checksum file intentionally covers all published binary assets.
    (args.output / "SHA256SUMS").write_text(
        f"{manifest['sha256']}  {artifact.name}\n{manifest['source_archive_sha256']}  {source.name}\n{digest(bundle)}  {bundle.name}\n",
        encoding="utf-8",
    )
    print(bundle)
    return 0


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