feat: add ops center and node onboarding flow
This commit is contained in:
151
verify_domain_release.sh
Executable file
151
verify_domain_release.sh
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
RELEASE_ROOT="${DOMAINCHECK_RELEASE_ROOT:-${ROOT_DIR}/release}"
|
||||
PACKAGE_PATH="${1:-}"
|
||||
HASH_PATH="${2:-}"
|
||||
|
||||
python3 - "${RELEASE_ROOT}" "${PACKAGE_PATH}" "${HASH_PATH}" <<'PY'
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
import tarfile
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
release_root = Path(sys.argv[1])
|
||||
package_path_arg = sys.argv[2].strip()
|
||||
hash_path_arg = sys.argv[3].strip()
|
||||
|
||||
|
||||
def resolve_latest_package(root: Path) -> Path:
|
||||
candidates = sorted(
|
||||
[
|
||||
item
|
||||
for item in root.glob("*")
|
||||
if item.is_file() and (item.name.endswith(".tar.gz") or item.name.endswith(".zip"))
|
||||
],
|
||||
key=lambda item: item.stat().st_mtime,
|
||||
reverse=True,
|
||||
)
|
||||
if not candidates:
|
||||
raise SystemExit("No release archive found")
|
||||
return candidates[0]
|
||||
|
||||
|
||||
def derive_hash_path(root: Path, package_path: Path) -> Path:
|
||||
if package_path.name.endswith(".tar.gz"):
|
||||
base_name = package_path.name[:-7]
|
||||
else:
|
||||
base_name = package_path.stem
|
||||
candidate = root / f"{base_name}.sha256.txt"
|
||||
if candidate.exists():
|
||||
return candidate
|
||||
candidates = sorted(root.glob("*.sha256.txt"), key=lambda item: item.stat().st_mtime, reverse=True)
|
||||
if not candidates:
|
||||
raise SystemExit("No hash file found")
|
||||
return candidates[0]
|
||||
|
||||
|
||||
def compute_sha256(path: Path) -> str:
|
||||
sha = hashlib.sha256()
|
||||
with path.open("rb") as handle:
|
||||
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
||||
sha.update(chunk)
|
||||
return sha.hexdigest().lower()
|
||||
|
||||
|
||||
def normalize_entry_names(names: list[str]) -> list[str]:
|
||||
normalized: list[str] = []
|
||||
for name in names:
|
||||
item = name.replace("\\", "/")
|
||||
while item.startswith("./"):
|
||||
item = item[2:]
|
||||
item = item.strip("/")
|
||||
if item:
|
||||
normalized.append(item)
|
||||
return normalized
|
||||
|
||||
|
||||
def read_archive_entries(path: Path) -> tuple[list[str], dict]:
|
||||
if path.name.endswith(".zip"):
|
||||
with zipfile.ZipFile(path, "r") as archive:
|
||||
entry_names = normalize_entry_names(archive.namelist())
|
||||
manifest = {}
|
||||
if "release_manifest.json" in entry_names:
|
||||
with archive.open("release_manifest.json", "r") as handle:
|
||||
manifest = json.loads(handle.read().decode("utf-8"))
|
||||
return entry_names, manifest
|
||||
|
||||
with tarfile.open(path, "r:*") as archive:
|
||||
entry_names = normalize_entry_names(archive.getnames())
|
||||
manifest = {}
|
||||
if "release_manifest.json" in entry_names:
|
||||
manifest_member = archive.extractfile("./release_manifest.json") or archive.extractfile("release_manifest.json")
|
||||
if manifest_member is not None:
|
||||
manifest = json.loads(manifest_member.read().decode("utf-8"))
|
||||
return entry_names, manifest
|
||||
|
||||
|
||||
package_path = Path(package_path_arg) if package_path_arg else resolve_latest_package(release_root)
|
||||
if not package_path.exists():
|
||||
raise SystemExit(f"archive not found: {package_path}")
|
||||
|
||||
hash_path = Path(hash_path_arg) if hash_path_arg else derive_hash_path(release_root, package_path)
|
||||
if not hash_path.exists():
|
||||
raise SystemExit(f"hash file not found: {hash_path}")
|
||||
|
||||
expected_hash = ""
|
||||
expected_file = ""
|
||||
for line in hash_path.read_text(encoding="utf-8").splitlines():
|
||||
if line.startswith("sha256="):
|
||||
expected_hash = line.split("=", 1)[1].strip().lower()
|
||||
elif line.startswith("file="):
|
||||
expected_file = line.split("=", 1)[1].strip()
|
||||
|
||||
if not expected_hash:
|
||||
raise SystemExit(f"sha256 entry missing in {hash_path}")
|
||||
|
||||
actual_hash = compute_sha256(package_path)
|
||||
entry_names, manifest = read_archive_entries(package_path)
|
||||
entry_set = set(entry_names)
|
||||
|
||||
required_entries = [
|
||||
"README_RELEASE.txt",
|
||||
"release_manifest.json",
|
||||
"domain-api/README.md",
|
||||
"domain-web/README.md",
|
||||
"scripts/package_domain_release.ps1",
|
||||
"scripts/verify_domain_release.ps1",
|
||||
"scripts/package_domain_release.sh",
|
||||
"scripts/verify_domain_release.sh",
|
||||
"scripts/smoke_test_stack.ps1",
|
||||
]
|
||||
|
||||
missing_entries = [item for item in required_entries if item not in entry_set]
|
||||
|
||||
smoke_report = str(((manifest.get("smoke_test") or {}).get("report") or "")).strip()
|
||||
if smoke_report and smoke_report not in entry_set:
|
||||
missing_entries.append(smoke_report)
|
||||
|
||||
ok = (
|
||||
actual_hash == expected_hash
|
||||
and not missing_entries
|
||||
and (not expected_file or expected_file == package_path.name)
|
||||
)
|
||||
|
||||
report = {
|
||||
"ok": ok,
|
||||
"archive": str(package_path),
|
||||
"hash_file": str(hash_path),
|
||||
"expected_file": expected_file,
|
||||
"actual_file": package_path.name,
|
||||
"expected_sha256": expected_hash,
|
||||
"actual_sha256": actual_hash,
|
||||
"missing_entries": missing_entries,
|
||||
"package_type": "tar.gz" if package_path.name.endswith(".tar.gz") else "zip",
|
||||
}
|
||||
print(json.dumps(report, ensure_ascii=False, indent=2))
|
||||
raise SystemExit(0 if ok else 1)
|
||||
PY
|
||||
Reference in New Issue
Block a user