初始化奇妙小屏幕控制器项目

This commit is contained in:
2026-09-08 22:56:52 +08:00
commit 8d368de3b5
491 changed files with 67678 additions and 0 deletions
@@ -0,0 +1,174 @@
"""Promote an exact, real-card-validated image candidate without rebuilding it."""
from __future__ import annotations
from datetime import datetime, timezone
import hashlib
import json
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import uuid
from app.ota.versioning import SoftwareVersion, read_software_version
from scripts.build_sd_image import build_bundle, sha256_file
from scripts.fat16_image import Fat16Image
from scripts.image_config import read_config_file
REPORT_FIELDS = {
"schema_version", "artifact_type", "image_sha256", "software_version", "status",
"firstboot_completed", "automatic_reboot_passed", "default_wifi_connected",
"ssh_password_login_passed", "sudo_password_passed", "root_login_rejected",
"networkmanager_passed", "controller_service_passed", "kernel_health_passed",
"h618_mapping_passed", "web_ui_passed", "plaintext_config_removed",
"machine_identity_regenerated", "second_reboot_passed", "validated_at",
}
PASS_FIELDS = REPORT_FIELDS - {
"schema_version", "artifact_type", "image_sha256", "software_version", "status", "validated_at"
}
def _validate_report(path: Path, digest: str, version: SoftwareVersion) -> dict:
report = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(report, dict) or set(report) != REPORT_FIELDS:
raise ValueError("image validation report fields are invalid")
try:
validated_at = datetime.fromisoformat(report.get("validated_at", ""))
except (TypeError, ValueError) as exc:
raise ValueError("image validation timestamp is invalid") from exc
if (
report["schema_version"] != 1
or report["artifact_type"] != "image"
or report["image_sha256"] != digest
or report["software_version"] != str(version)
or report["status"] != "success"
or validated_at.tzinfo is None
or any(report[name] is not True for name in PASS_FIELDS)
):
raise ValueError("candidate lacks matching successful real-card validation")
return report
def promote(source: Path, candidate: Path, validation: Path, notes: str) -> Path:
from scripts.export_release import (
_atomic_bytes, _atomic_json, _copy_source, _history, _image_readme, next_patch,
)
source = Path(source).resolve()
project = source.parent
candidate = Path(candidate).resolve()
validation = Path(validation).resolve()
lock = project / ".release-export.lock"
descriptor = os.open(lock, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
os.close(descriptor)
archive: Path | None = None
staged: Path | None = None
final: Path | None = None
published = False
history_path = project / "发布记录.json"
history_original = history_path.read_bytes()
version_original = (source / "VERSION").read_bytes()
try:
if not candidate.is_dir():
raise ValueError("validated image candidate directory is missing")
manifest = json.loads((candidate / "manifest.json").read_text(encoding="utf-8"))
version = SoftwareVersion.parse(manifest.get("software_version", ""))
current = read_software_version(source)
if version != next_patch(current):
raise ValueError("validated image candidate must be the next patch version")
artifact_name = f"matrix-screen-controller-{version}.img"
if {path.name for path in candidate.iterdir()} != {
"README.md", "manifest.json", artifact_name, f"{artifact_name}.sha256",
}:
raise ValueError("image candidate directory contents are not exact")
artifact = candidate / artifact_name
digest = sha256_file(artifact)
if (
manifest.get("artifact_type") != "image"
or manifest.get("artifact") != artifact_name
or manifest.get("image_sha256") != digest
or manifest.get("image_bytes") != artifact.stat().st_size
or manifest.get("notes") != notes.strip()
or (candidate / f"{artifact_name}.sha256").read_bytes()
!= f"{digest} {artifact_name}\n".encode("ascii")
):
raise ValueError("image candidate metadata does not match its artifact")
with Fat16Image(artifact) as image:
config_bytes = image.read_file("MSCCFG.BIN")
config, _generation, _slot = read_config_file(config_bytes)
if config["software_version"] != str(version):
raise ValueError("image candidate configuration version is invalid")
if manifest.get("config_sha256") != hashlib.sha256(config_bytes).hexdigest():
raise ValueError("image candidate configuration digest is invalid")
if (candidate / "README.md").read_text(encoding="utf-8") != _image_readme(version, manifest, notes, config):
raise ValueError("image candidate README does not match its embedded defaults")
_validate_report(validation, digest, version)
dependency_root = project / "发布更新相关" / "其他依赖"
kernel = dependency_root / "aarch64-kernel" / "6.1.31-matrix-axp313a1"
with tempfile.TemporaryDirectory(prefix="matrix-image-promotion-") as temporary_text:
temporary = Path(temporary_text)
staged_source = temporary / "核桃派软件源代码"
_copy_source(source, staged_source, version)
expected_bundle = temporary / "MSCBOOT.TGZ"
build_bundle(
staged_source,
dependency_root / "aarch64-py311",
dependency_root / "debian12-aarch64",
expected_bundle,
dependency_root / "frp" / "0.71.0" / "linux-arm64",
)
subprocess.run(
[
"bash", str(staged_source / "scripts" / "verify_image_bootstrap.sh"),
str(artifact), str(staged_source / "systemd" / "matrix-image-firstboot.service"),
str(staged_source / "systemd" / "10-walnutpi-screen-hardening.conf"),
str(kernel), str(expected_bundle),
],
check=True,
)
history = _history(history_path)
if any(record.get("version") == str(version) for record in history["releases"]):
raise ValueError("validated image version is already registered")
final = project / "发布更新相关" / "导出包" / str(version)
if final.exists():
raise ValueError("formal image release directory already exists")
stamp = datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds")
archive = project / "各种归档" / f"{datetime.now().strftime('%Y%m%d_%H%M%S')}_IMAGE{version}实卡验收_{uuid.uuid4().hex[:6]}"
archive.mkdir(parents=True)
shutil.copy2(validation, archive / "实机验收.json")
staged = final.parent / f".{version}.{uuid.uuid4().hex}.publishing"
shutil.copytree(candidate, staged)
if sha256_file(staged / artifact_name) != digest:
raise ValueError("candidate copy checksum mismatch")
history["releases"].append({
"version": str(version),
"artifact_type": "image",
"created_at": manifest["created_at"],
"notes": notes.strip(),
"artifact_path": f"发布更新相关/导出包/{version}/{artifact_name}",
"artifact_sha256": digest,
"validation_path": f"{archive.relative_to(project).as_posix()}/实机验收.json",
"validated_at": stamp,
})
os.replace(staged, final)
staged = None
published = True
_atomic_json(history_path, history)
_atomic_bytes(source / "VERSION", f"{version}\n".encode("utf-8"))
return final
except BaseException:
if staged is not None:
shutil.rmtree(staged, ignore_errors=True)
if published and final is not None:
shutil.rmtree(final, ignore_errors=True)
_atomic_bytes(history_path, history_original)
_atomic_bytes(source / "VERSION", version_original)
if archive is not None:
shutil.rmtree(archive, ignore_errors=True)
raise
finally:
lock.unlink(missing_ok=True)