254 lines
8.7 KiB
Python
254 lines
8.7 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Create deterministic, offline-only Phase-0.7 review archives."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import shutil
|
|
import subprocess
|
|
import zipfile
|
|
from pathlib import Path
|
|
|
|
|
|
ARCHIVE_TIMESTAMP = (2026, 7, 17, 0, 0, 0)
|
|
ARTIFACTS = {
|
|
"chimera-elfldr-phase07.elf": {
|
|
"source": "outputs/phase07/artifacts/chimera-elfldr-phase07-a.elf",
|
|
"sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561",
|
|
"size": 397000,
|
|
},
|
|
"chimera-gfx-lifecycle-probe.elf": {
|
|
"source": "outputs/phase07/artifacts/chimera-gfx-lifecycle-probe.elf",
|
|
"sha256": "bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182",
|
|
"size": 112680,
|
|
},
|
|
"chimera-payload-manager-phase07.elf": {
|
|
"source": "outputs/phase07/artifacts/chimera-pldmgr-phase07-a.elf",
|
|
"sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1",
|
|
"size": 99560,
|
|
},
|
|
}
|
|
STOCK_ELFLDR = {
|
|
"source": "work/upstream/release-assets/elfldr-ps5-v0.23.elf",
|
|
"sha256": "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8",
|
|
"size": 397000,
|
|
}
|
|
BLOCKED_SHA256 = (
|
|
"4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63"
|
|
)
|
|
|
|
|
|
def sha256(path: Path) -> str:
|
|
digest = hashlib.sha256()
|
|
with path.open("rb") as stream:
|
|
for block in iter(lambda: stream.read(1024 * 1024), b""):
|
|
digest.update(block)
|
|
return digest.hexdigest()
|
|
|
|
|
|
def verify(path: Path, expected: dict[str, object]) -> None:
|
|
if not path.is_file():
|
|
raise RuntimeError(f"missing package input: {path}")
|
|
if path.stat().st_size != expected["size"]:
|
|
raise RuntimeError(f"unexpected size: {path}")
|
|
if sha256(path) != expected["sha256"]:
|
|
raise RuntimeError(f"unexpected SHA-256: {path}")
|
|
|
|
|
|
def copy(root: Path, relative_source: str, destination: Path) -> None:
|
|
source = root / relative_source
|
|
if not source.is_file():
|
|
raise RuntimeError(f"missing package input: {source}")
|
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copyfile(source, destination)
|
|
|
|
|
|
def write_json(path: Path, document: object) -> None:
|
|
path.write_text(
|
|
json.dumps(document, indent=2, sort_keys=True) + "\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
|
|
def write_sums(directory: Path) -> None:
|
|
entries = []
|
|
for path in sorted(directory.rglob("*")):
|
|
if path.is_file() and path.name != "SHA256SUMS.txt":
|
|
relative = path.relative_to(directory).as_posix()
|
|
entries.append(f"{sha256(path)} {relative}")
|
|
(directory / "SHA256SUMS.txt").write_text(
|
|
"\n".join(entries) + "\n", encoding="utf-8", newline="\n"
|
|
)
|
|
|
|
|
|
def deterministic_zip(source: Path, destination: Path) -> None:
|
|
with zipfile.ZipFile(
|
|
destination, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9
|
|
) as archive:
|
|
for path in sorted(source.rglob("*")):
|
|
if not path.is_file():
|
|
continue
|
|
relative = (Path(source.name) / path.relative_to(source)).as_posix()
|
|
info = zipfile.ZipInfo(relative, ARCHIVE_TIMESTAMP)
|
|
info.compress_type = zipfile.ZIP_DEFLATED
|
|
info.external_attr = 0o100644 << 16
|
|
archive.writestr(info, path.read_bytes(), compresslevel=9)
|
|
|
|
|
|
def clean_destination(root: Path, destination: Path) -> None:
|
|
resolved_root = root.resolve()
|
|
resolved = destination.resolve()
|
|
if resolved_root not in resolved.parents:
|
|
raise RuntimeError(f"refusing destination outside repository: {resolved}")
|
|
if destination.exists():
|
|
shutil.rmtree(destination)
|
|
|
|
|
|
def git_head(root: Path) -> str:
|
|
status = subprocess.run(
|
|
["git", "status", "--porcelain", "--untracked-files=no"],
|
|
cwd=root,
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if status.stdout.strip():
|
|
raise RuntimeError("tracked worktree must be clean before packaging")
|
|
result = subprocess.run(
|
|
["git", "rev-parse", "HEAD"],
|
|
cwd=root,
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
return result.stdout.strip()
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--root", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
root = args.root.resolve()
|
|
head = git_head(root)
|
|
packages = root / "outputs" / "phase07" / "packages"
|
|
installation = packages / "phase07-installation-review"
|
|
rollback = packages / "phase07-rollback-review"
|
|
clean_destination(root, installation)
|
|
clean_destination(root, rollback)
|
|
packages.mkdir(parents=True, exist_ok=True)
|
|
|
|
for filename, expected in ARTIFACTS.items():
|
|
source = root / str(expected["source"])
|
|
verify(source, expected)
|
|
copy(root, str(expected["source"]), installation / "artifacts" / filename)
|
|
|
|
review_files = [
|
|
"docs/approvals/phase07-hardened-installation-request.md",
|
|
"docs/approvals/phase07-lifecycle-transfer-execution-request.md",
|
|
"docs/runtime/phase-0.7-hardening.md",
|
|
"docs/runtime/controlled-runtime-policy.md",
|
|
"manifests/artifact-denylist.json",
|
|
"manifests/artifacts/chimera-elfldr-phase07-fw-9.60.json",
|
|
"manifests/artifacts/chimera-gfx-lifecycle-probe-phase07-fw-9.60.json",
|
|
"manifests/artifacts/chimera-payload-manager-phase07-fw-9.60.json",
|
|
"manifests/runtime/controlled-ps5-runtime-profile.json",
|
|
"manifests/runtime/phase-0.7-kernelwrite-proof-matrix.json",
|
|
"manifests/runtime/phase-0.7-offline-audit.json",
|
|
"packaging/phase07/README-installation-review.md",
|
|
]
|
|
for relative in review_files:
|
|
copy(root, relative, installation / relative)
|
|
audit_directory = root / "outputs" / "phase07" / "audit"
|
|
if not audit_directory.is_dir():
|
|
raise RuntimeError("full Phase-0.7 audit reports are absent")
|
|
(installation / "audit").mkdir(parents=True, exist_ok=True)
|
|
for report in sorted(audit_directory.glob("*")):
|
|
if report.is_file():
|
|
shutil.copyfile(report, installation / "audit" / report.name)
|
|
|
|
install_manifest = {
|
|
"artifacts": {
|
|
name: {
|
|
"sha256": item["sha256"],
|
|
"size": item["size"],
|
|
}
|
|
for name, item in ARTIFACTS.items()
|
|
},
|
|
"blocked_sha256": BLOCKED_SHA256,
|
|
"decision": "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT",
|
|
"execution_authorized": False,
|
|
"firmware": "9.60",
|
|
"installation_authorized": False,
|
|
"repository_head": head,
|
|
"schema_version": 1,
|
|
}
|
|
write_json(installation / "PACKAGE-MANIFEST.json", install_manifest)
|
|
write_sums(installation)
|
|
|
|
stock_source = root / str(STOCK_ELFLDR["source"])
|
|
verify(stock_source, STOCK_ELFLDR)
|
|
copy(
|
|
root,
|
|
str(STOCK_ELFLDR["source"]),
|
|
rollback / "stock" / "elfldr-ps5-v0.23.elf",
|
|
)
|
|
copy(
|
|
root,
|
|
"packaging/phase07/README-rollback.md",
|
|
rollback / "README-rollback.md",
|
|
)
|
|
rollback_manifest = {
|
|
"existing_payload_manager_backup": {
|
|
"available_offline": False,
|
|
"required_before_installation": True,
|
|
"sha256": "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b",
|
|
"size": 2050320,
|
|
},
|
|
"installation_authorized": False,
|
|
"repository_head": head,
|
|
"rollback_authorized": False,
|
|
"schema_version": 1,
|
|
"stock_elfldr": {
|
|
"available_offline": True,
|
|
"sha256": STOCK_ELFLDR["sha256"],
|
|
"size": STOCK_ELFLDR["size"],
|
|
},
|
|
}
|
|
write_json(rollback / "ROLLBACK-MANIFEST.json", rollback_manifest)
|
|
write_sums(rollback)
|
|
|
|
installation_zip = packages / "phase07-installation-review.zip"
|
|
rollback_zip = packages / "phase07-rollback-review.zip"
|
|
for archive in (installation_zip, rollback_zip):
|
|
if archive.exists():
|
|
archive.unlink()
|
|
deterministic_zip(installation, installation_zip)
|
|
deterministic_zip(rollback, rollback_zip)
|
|
package_index = {
|
|
"archives": {
|
|
installation_zip.name: {
|
|
"sha256": sha256(installation_zip),
|
|
"size": installation_zip.stat().st_size,
|
|
},
|
|
rollback_zip.name: {
|
|
"sha256": sha256(rollback_zip),
|
|
"size": rollback_zip.stat().st_size,
|
|
},
|
|
},
|
|
"execution_authorized": False,
|
|
"installation_authorized": False,
|
|
"repository_head": head,
|
|
"schema_version": 1,
|
|
}
|
|
write_json(packages / "package-index.json", package_index)
|
|
print(json.dumps(package_index, sort_keys=True))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|