Archive one-time physical Fifth cutover script
This commit is contained in:
parent
c4327e710d
commit
6eb326a2e4
2 changed files with 157 additions and 0 deletions
|
|
@ -266,6 +266,18 @@
|
||||||
"intake_method": "VERIFIED_COPY_BEFORE_IN_PLACE_STUB",
|
"intake_method": "VERIFIED_COPY_BEFORE_IN_PLACE_STUB",
|
||||||
"old_path_status": "READ_ONLY_SNAPSHOT_ONLY",
|
"old_path_status": "READ_ONLY_SNAPSHOT_ONLY",
|
||||||
"restoration": "EXPLICIT_OWNER_REQUEST_AND_NEW_VERIFIED_ROUTE_REQUIRED"
|
"restoration": "EXPLICIT_OWNER_REQUEST_AND_NEW_VERIFIED_ROUTE_REQUIRED"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"object_id": "physical-fifth-cutover-20260924",
|
||||||
|
"source_path": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/CODEX-DEV-20260915/光湖语言世界/运行时/physical-fifth-cutover.py",
|
||||||
|
"archive_path": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/CODEX-DEV-20260915/光湖第五域/13-归档信息大楼/physical-fifth-cutover-20260924.py",
|
||||||
|
"sha256": "121418c9d101c387db6119e69bb221ca0cf3c8cf06cc2ed85e49c218f1c05a2f",
|
||||||
|
"bytes": 6995,
|
||||||
|
"lifecycle": "ONE_TIME_PHYSICAL_MIGRATION_EXECUTED_HISTORY_ONLY",
|
||||||
|
"current_route": false,
|
||||||
|
"intake_method": "SAME_VOLUME_MOVE",
|
||||||
|
"old_path_status": "REMOVED_ONE_TIME_SCRIPT",
|
||||||
|
"restoration": "EXPLICIT_OWNER_REQUEST_AND_NEW_VERIFIED_ROUTE_REQUIRED"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"historical_blueprint": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/CODEX-DEV-20260913/world-source-tcs-planet-five-domain-mother-separation/transport/source/gls/source-purification/archive-information-building/ARCHIVE-BUILDING-INDEX.hdlp"
|
"historical_blueprint": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/CODEX-DEV-20260913/world-source-tcs-planet-five-domain-mother-separation/transport/source/gls/source-purification/archive-information-building/ARCHIVE-BUILDING-INDEX.hdlp"
|
||||||
|
|
|
||||||
145
13-归档信息大楼/physical-fifth-cutover-20260924.py
Normal file
145
13-归档信息大楼/physical-fifth-cutover-20260924.py
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""One-time, rollback-safe physical Fifth domain cutover on the local APFS volume."""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
|
WORLD = Path(__file__).resolve().parents[1]
|
||||||
|
OLD = WORLD / "05-第五域"
|
||||||
|
NEW = WORLD.parent / "光湖第五域"
|
||||||
|
LABELS = (
|
||||||
|
"com.guanghu.zhuyuan-brain-service",
|
||||||
|
"com.guanghu.shuangyan-local-brain",
|
||||||
|
"com.guanghu.zy-brain-mirror-sync-v2",
|
||||||
|
"com.guanghu.world-evidence-observer",
|
||||||
|
)
|
||||||
|
PLIST_DIR = Path.home() / "Library/LaunchAgents"
|
||||||
|
POINTER_NAMES = (
|
||||||
|
"WORLD-INDEX.json",
|
||||||
|
"ROUTE-REGISTRY.json",
|
||||||
|
"光湖语言世界-2.0-四域平行世界根-20260924.tcs",
|
||||||
|
"光湖语言世界-2.0-四域平行世界根-20260924.gir.json",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run(*args, check=True):
|
||||||
|
return subprocess.run(args, text=True, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT, check=check)
|
||||||
|
|
||||||
|
|
||||||
|
def replace_bytes(path, data):
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with tempfile.NamedTemporaryFile(dir=path.parent, prefix=f".{path.name}.", delete=False) as f:
|
||||||
|
f.write(data)
|
||||||
|
tmp = Path(f.name)
|
||||||
|
os.replace(tmp, path)
|
||||||
|
|
||||||
|
|
||||||
|
def candidate_dir():
|
||||||
|
return OLD / "10-语言源代码回声系统/心跳核心语言架构/GLW2-ENTRY-CLOSURE-20260923/PHYSICAL-FIFTH-MOVE-20260924"
|
||||||
|
|
||||||
|
|
||||||
|
def sha(data):
|
||||||
|
return hashlib.sha256(data).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not OLD.is_dir() or OLD.is_symlink() or NEW.exists() or NEW.is_symlink():
|
||||||
|
raise RuntimeError("SOURCE_OR_DESTINATION_NOT_AS_EXPECTED")
|
||||||
|
if OLD.stat().st_dev != WORLD.parent.stat().st_dev:
|
||||||
|
raise RuntimeError("NOT_SAME_VOLUME_ATOMIC_RENAME_UNAVAILABLE")
|
||||||
|
|
||||||
|
candidate = candidate_dir()
|
||||||
|
targets = {WORLD / name: (candidate / (name + ".next") if name.endswith(".json") and name in ("WORLD-INDEX.json", "ROUTE-REGISTRY.json", "ZEROPOINT-CHANNEL.json") else candidate / name).read_bytes() for name in POINTER_NAMES}
|
||||||
|
targets[WORLD / "00-零点原核/零点原核频道/ZEROPOINT-CHANNEL.json"] = (candidate / "ZEROPOINT-CHANNEL.json.next").read_bytes()
|
||||||
|
adapters_rel = "03-冰朔通感语言核系统/模块实验频道/MODULE-ADAPTERS.json"
|
||||||
|
observer_rel = "10-语言源代码回声系统/世界起源与历史存证/closure-20260923/observer-config.json"
|
||||||
|
targets[NEW / adapters_rel] = (candidate / "MODULE-ADAPTERS.json.next").read_bytes()
|
||||||
|
targets[NEW / observer_rel] = (candidate / "observer-config.json.next").read_bytes()
|
||||||
|
evidence_policy = json.loads((WORLD / "运行时/world-evidence-policy.json").read_text())
|
||||||
|
if not evidence_policy["evidence_root"].startswith("05-第五域/") or not evidence_policy["brain_log"].startswith("05-第五域/"):
|
||||||
|
raise RuntimeError("EVIDENCE_POLICY_SOURCE_SHAPE_CHANGED")
|
||||||
|
mounted_policy = {**evidence_policy,
|
||||||
|
"evidence_root": "光湖第五域/" + evidence_policy["evidence_root"].removeprefix("05-第五域/"),
|
||||||
|
"brain_log": "光湖第五域/" + evidence_policy["brain_log"].removeprefix("05-第五域/")}
|
||||||
|
targets[WORLD / "运行时/world-evidence-policy-mounted.json"] = (json.dumps(mounted_policy, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n").encode()
|
||||||
|
for label in LABELS:
|
||||||
|
targets[PLIST_DIR / (label + ".plist")] = (candidate / "plists" / (label + ".plist")).read_bytes()
|
||||||
|
|
||||||
|
index = json.loads(targets[WORLD / "WORLD-INDEX.json"])
|
||||||
|
route = json.loads(targets[WORLD / "ROUTE-REGISTRY.json"])
|
||||||
|
if index["tcs_root_topology"]["physical_roots"]["fifth_domain"] != "../光湖第五域/":
|
||||||
|
raise RuntimeError("CANDIDATE_INDEX_NOT_PARALLEL")
|
||||||
|
if not route or b"../\xe5\x85\x89\xe6\xb9\x96\xe7\xac\xac\xe4\xba\x94\xe5\x9f\x9f/" not in targets[WORLD / "ROUTE-REGISTRY.json"]:
|
||||||
|
raise RuntimeError("CANDIDATE_ROUTE_NOT_PARALLEL")
|
||||||
|
|
||||||
|
originals = {p: (OLD / p.relative_to(NEW) if p.is_relative_to(NEW) else p).read_bytes() for p in targets}
|
||||||
|
backup = WORLD / "运行时/state/physical-fifth-cutover-20260924"
|
||||||
|
backup.mkdir(parents=True, exist_ok=True)
|
||||||
|
manifest = []
|
||||||
|
for p, data in originals.items():
|
||||||
|
name = sha(str(p).encode()) + ".before"
|
||||||
|
saved = backup / name
|
||||||
|
if saved.exists() and saved.read_bytes() != data:
|
||||||
|
raise RuntimeError("BACKUP_COLLISION_OR_INPUT_DRIFT")
|
||||||
|
if not saved.exists():
|
||||||
|
saved.write_bytes(data)
|
||||||
|
manifest.append({"path_before": str(p), "sha256": sha(data), "backup": str(saved)})
|
||||||
|
(backup / "MANIFEST.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n")
|
||||||
|
|
||||||
|
uid = str(os.getuid())
|
||||||
|
stopped = []
|
||||||
|
moved = False
|
||||||
|
mounted = False
|
||||||
|
try:
|
||||||
|
for label in LABELS:
|
||||||
|
p = PLIST_DIR / (label + ".plist")
|
||||||
|
result = run("launchctl", "bootout", f"gui/{uid}", str(p), check=False)
|
||||||
|
if result.returncode != 0 and "Could not find service" not in result.stdout:
|
||||||
|
raise RuntimeError(f"BOOTOUT_FAILED {label}: {result.stdout.strip()}")
|
||||||
|
stopped.append(label)
|
||||||
|
os.rename(OLD, NEW)
|
||||||
|
moved = True
|
||||||
|
OLD.symlink_to(Path("..") / "光湖第五域", target_is_directory=True)
|
||||||
|
mounted = True
|
||||||
|
for p, data in targets.items():
|
||||||
|
replace_bytes(p, data)
|
||||||
|
probes = (
|
||||||
|
("node", str(WORLD / "运行时/first-glance.mjs")),
|
||||||
|
("node", str(WORLD / "运行时/brain-core.mjs"), "verify"),
|
||||||
|
("node", str(WORLD / "运行时/world-evidence.mjs")),
|
||||||
|
)
|
||||||
|
for probe in probes:
|
||||||
|
result = run(*probe, check=False)
|
||||||
|
if result.returncode:
|
||||||
|
raise RuntimeError(f"PROBE_FAILED {probe}: {result.stdout[:3000]}")
|
||||||
|
print("PROBE_PASS", probe[-1], result.stdout[:350].replace("\n", " "), flush=True)
|
||||||
|
for label in LABELS:
|
||||||
|
p = PLIST_DIR / (label + ".plist")
|
||||||
|
run("launchctl", "bootstrap", f"gui/{uid}", str(p))
|
||||||
|
status = {"state": "PHYSICAL_FIFTH_MOVED_PARALLEL_VERIFIED", "old_mount": str(OLD), "new_root": str(NEW), "pointer_sha256": {str(p): sha(data) for p, data in targets.items()}, "backup_manifest": str(backup / "MANIFEST.json")}
|
||||||
|
(backup / "CUTOVER-READBACK.json").write_text(json.dumps(status, ensure_ascii=False, indent=2) + "\n")
|
||||||
|
print(json.dumps(status, ensure_ascii=False), flush=True)
|
||||||
|
except Exception:
|
||||||
|
for label in reversed(LABELS):
|
||||||
|
run("launchctl", "bootout", f"gui/{uid}", str(PLIST_DIR / (label + ".plist")), check=False)
|
||||||
|
for p, data in originals.items():
|
||||||
|
replace_bytes(OLD / p.relative_to(NEW) if p.is_relative_to(NEW) and moved else p, data)
|
||||||
|
if mounted and OLD.is_symlink():
|
||||||
|
OLD.unlink()
|
||||||
|
if moved and NEW.is_dir():
|
||||||
|
os.rename(NEW, OLD)
|
||||||
|
for label in stopped:
|
||||||
|
run("launchctl", "bootstrap", f"gui/{uid}", str(PLIST_DIR / (label + ".plist")), check=False)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in a new issue