omniAI / scripts /package_release.py
hasimjaneef's picture
Publish validated findings before turn end and trace pipeline latency
79e11ca verified
Raw History Blame Contribute Delete
6.68 kB
"""Allowlisted consistent HF packages. Never uploads; never packages arbitrary cwd files."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import shutil
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
DIRS = ("backend", "frontend", "gpu_service", "docs", "deploy", "scripts", "tests")
FILES = ("README.md", "Dockerfile", ".dockerignore", ".gitignore", ".env.example", "pyproject.toml",
"requirements.txt", "requirements-dev.txt", "requirements-gpu.txt", "constraints-cpu.txt", "model-lock.json")
EXCLUDED = {"node_modules", "dist", "__pycache__", ".pytest_cache", ".ruff_cache", ".venv", "output", "private-media"}
ALLOWED = {".py", ".md", ".txt", ".json", ".html", ".css", ".ts", ".tsx", ".js", ".sh", ".ttf", ".toml", ".Dockerfile"}
REQUIRED = ("backend/main.py", "backend/session.py", "backend/perception.py", "backend/portfolio.py",
"backend/public_output.py", "backend/settings.py", "backend/schemas.py",
"frontend/package.json", "frontend/package-lock.json", "frontend/index.html",
"frontend/vite.config.ts", "frontend/src/App.tsx", "frontend/src/api.ts", "frontend/src/styles.css",
"frontend/src/mediaCapture.ts", "frontend/src/transport.ts", "frontend/src/audio.ts", "frontend/src/latency.ts",
"frontend/public/audio-worklet.js", "frontend/src/components/ObservatoryLogo.tsx",
"frontend/src/components/InstrumentPicker.tsx", "frontend/src/instruments.ts",
"frontend/src/components/PortfolioEditor.tsx", "frontend/src/portfolio.ts",
"frontend/src/instrumentSearch.ts", "frontend/src/catalog/equities.json",
"frontend/src/catalog/funds.json", "frontend/src/catalog/maritime.json",
"frontend/src/assets/manrope/Manrope-Variable.ttf", "frontend/src/assets/manrope/OFL.txt",
"frontend/public/licenses/Manrope-OFL.txt", "gpu_service/app.py", "gpu_service/native.py",
"gpu_service/contracts.py", "gpu_service/smoke.py", "requirements.txt",
"requirements-gpu.txt", "constraints-cpu.txt", "deploy/gpu.Dockerfile",
"docs/model-interface.md", "docs/architecture.md", "docs/hugging-face-delivery.md",
"docs/verification.md", "model-lock.json", "scripts/verify_gpu_install.py")
SECRET = re.compile(rb"\b(?:hf_[A-Za-z0-9]{16,}|sk-[A-Za-z0-9_-]{20,})\b")
def files():
result = []
for directory in DIRS:
for path in (ROOT / directory).rglob("*"):
relative = path.relative_to(ROOT)
if set(relative.parts) & EXCLUDED or path.name.endswith(".tsbuildinfo"):
continue
if path.is_symlink():
raise RuntimeError("Symlink is not allowed in release: " + str(relative))
if path.is_dir():
continue
if path.name.startswith(".env"):
raise RuntimeError("Runtime environment file cannot be released: " + str(relative))
if path.suffix not in ALLOWED and path.name != "Dockerfile":
raise RuntimeError("Unexpected file in release directory: " + str(relative))
result.append(path)
result.extend(ROOT / name for name in FILES)
for relative in REQUIRED:
if ROOT / relative not in result:
raise RuntimeError("Required application file missing: " + relative)
return sorted(set(result))
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--validate-only", action="store_true")
parser.add_argument("--output", type=Path, default=ROOT / "release")
args = parser.parse_args()
selected = files()
manifest = {}
for source in selected:
data = source.read_bytes()
# Secret-shaped fixture rejection strings are written via concatenation
# in tests, so the exact package never contains usable token-shaped text.
if SECRET.search(data):
raise RuntimeError("Credential-shaped value found; remove it before release: " + str(source.relative_to(ROOT)))
manifest[str(source.relative_to(ROOT))] = hashlib.sha256(data).hexdigest()
canonical = json.dumps(manifest, sort_keys=True, separators=(",", ":")).encode()
revision = hashlib.sha256(canonical).hexdigest()
try:
git_revision = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, stderr=subprocess.DEVNULL, text=True).strip()
except subprocess.CalledProcessError:
git_revision = None
if args.validate_only:
print(f"Package validation passed: {len(manifest)} allowlisted files; source SHA256 {revision}")
return
destination = args.output.resolve()
# Keep all writes inside an explicit package output folder; refuse workspace root.
if destination == ROOT or ROOT.is_relative_to(destination):
raise RuntimeError("Choose a release output subdirectory, not the source root")
for flavor in ("app", "gpu"):
stage = destination / flavor
if stage.exists():
shutil.rmtree(stage)
stage.mkdir(parents=True)
for source in selected:
target = stage / source.relative_to(ROOT)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(source, target)
if hashlib.sha256(target.read_bytes()).hexdigest() != manifest[str(source.relative_to(ROOT))]:
raise RuntimeError("Source changed during packaging; rerun after edits finish")
if flavor == "gpu":
shutil.copyfile(stage / "deploy/gpu.Dockerfile", stage / "Dockerfile")
(stage / "README.md").write_text("---\ntitle: OmnAI MiniCPM-o 4.5\nsdk: docker\napp_port: 8090\n---\n\nPersistent authenticated MiniCPM-o 4.5 audiovisual and portfolio research service. One model, one ongoing native duplex session. See docs/model-interface.md, docs/architecture.md and docs/hugging-face-delivery.md.\n\nSource package SHA256: " + revision + "\n")
package_files = {str(p.relative_to(stage)): hashlib.sha256(p.read_bytes()).hexdigest()
for p in stage.rglob("*") if p.is_file()}
record = {"source_sha256": revision, "git_revision": git_revision, "flavor": flavor,
"source_files": manifest, "package_files": package_files}
(stage / "RELEASE_MANIFEST.json").write_text(json.dumps(record, indent=2) + "\n")
for name, digest in package_files.items():
assert hashlib.sha256((stage / name).read_bytes()).hexdigest() == digest
print(f"Validated app and GPU packages: {destination}\nShared source SHA256: {revision}")
if __name__ == "__main__":
main()