Spaces:
Running
Running
Deploy Aureon OS (deploy/selfhost stack: nginx edge + fusion API x2 + console + HNC organ + redis + postgres; paper mode) (part 7)
c626614 verified Download deployment/google-cloud/build_release_bundle.py from GaryLeckey/aureon-os: direct link, hf CLI and curl.
- Browser
- Download file 26 kB
-
https://huggingface.co/spaces/GaryLeckey/aureon-os/resolve/main/deployment/google-cloud/build_release_bundle.py
- Command line
-
hf download hf://spaces/GaryLeckey/aureon-os/deployment/google-cloud/build_release_bundle.py
-
curl -L -o build_release_bundle.py https://huggingface.co/spaces/GaryLeckey/aureon-os/resolve/main/deployment/google-cloud/build_release_bundle.py
26 kB
| """Copy only an explicit reviewed source list into a new immutable release bundle.""" | |
| from __future__ import annotations | |
| import argparse | |
| import io | |
| import json | |
| import os | |
| import re | |
| import shutil | |
| import subprocess | |
| import tarfile | |
| import tempfile | |
| import time | |
| from datetime import UTC, datetime | |
| from pathlib import Path, PurePosixPath | |
| from release_update import (MAX_RELEASE_BYTES, SCHEMA, ReleaseError, confined, | |
| digest, durable_write, manifest_payload_sha256, | |
| read_bytes, source_path) | |
| from operator_runtime_closure import ( | |
| OPERATOR_COMPANION_SOURCES, | |
| OPERATOR_PACKAGE_PREFIX, | |
| OperatorRuntimeClosureError, | |
| build_operator_runtime_manifest, | |
| operator_package_runtime_sources, | |
| operator_runtime_requested, | |
| ) | |
| FRONTEND_DIST_PREFIX = "aureon-os-upstream/frontend/dist/" | |
| FRONTEND_DIST_INDEX = FRONTEND_DIST_PREFIX + "index.html" | |
| FRONTEND_SOURCE_ROOT = "aureon-os-upstream/frontend" | |
| FRONTEND_BUILD_SCHEMA = "aureon.frontend-reproducible-build.v1" | |
| FRONTEND_BUILD_RECIPE = ("npm ci --ignore-scripts --no-audit --no-fund", "npm run build") | |
| FRONTEND_REQUIRED_ENV = frozenset( | |
| {"VITE_REQUIRE_AUTH", "VITE_SUPABASE_URL", "VITE_SUPABASE_PUBLISHABLE_KEY"} | |
| ) | |
| FRONTEND_ALLOWED_ENV = FRONTEND_REQUIRED_ENV | frozenset( | |
| { | |
| "VITE_AUREON_MANIFEST_BASE", | |
| "VITE_NEWS_API_ENDPOINT", | |
| "VITE_NEWS_PROXY_URL", | |
| "VITE_NEXUS_WS_URL", | |
| "VITE_SUPPORT_PAYMENT_URLS", | |
| "VITE_SUPPORT_PROMPT_HOURS", | |
| "VITE_SUPPORT_SNOOZE_HOURS", | |
| } | |
| ) | |
| VM_RUNTIME_TRIGGER_SOURCES = frozenset( | |
| { | |
| "deployment/google-cloud/cloud_read_organ.py", | |
| "deployment/google-cloud/cloud_trade_receipt_collector.py", | |
| "deployment/google-cloud/cloud_order_intent_bridge.py", | |
| } | |
| ) | |
| TOKEN_BROKER_SOURCES = frozenset( | |
| { | |
| "deployment/google-cloud/aureon-cloud-token-broker.service", | |
| "deployment/google-cloud/vm_token_broker.py", | |
| "deployment/google-cloud/vm_workload_identity.py", | |
| } | |
| ) | |
| PUBLIC_BRIDGE_SOURCE = "deployment/google-cloud/public-entry/public_entry.py" | |
| PUBLIC_BRIDGE_UNIT_SOURCE = ( | |
| "deployment/google-cloud/public-entry/aureon-public-bridge.service" | |
| ) | |
| PUBLIC_BRIDGE_SOURCES = frozenset( | |
| {PUBLIC_BRIDGE_SOURCE, PUBLIC_BRIDGE_UNIT_SOURCE} | |
| ) | |
| BRIDGE_CERTIFICATE_RECEIPT_SCHEMA = ( | |
| "aureon.gcp.vm-public-bridge-certificate-receipt.v1" | |
| ) | |
| def _git(source: Path, *args: str) -> str: | |
| try: | |
| result = subprocess.run( | |
| ["git", "-C", str(source), *args], capture_output=True, text=True, | |
| timeout=30, check=False, | |
| ) | |
| except (OSError, subprocess.TimeoutExpired) as error: | |
| raise ReleaseError("source_git_identity_unavailable") from error | |
| if result.returncode != 0: | |
| raise ReleaseError("source_git_identity_unavailable") | |
| return result.stdout.strip() | |
| def _git_blob(source: Path, revision: str, relative: str) -> bytes: | |
| """Read exact committed bytes so bundle hashes ignore checkout EOL policy.""" | |
| try: | |
| result = subprocess.run( | |
| ["git", "-C", str(source), "cat-file", "blob", f"{revision}:{relative}"], | |
| capture_output=True, | |
| timeout=30, | |
| check=False, | |
| ) | |
| except (OSError, subprocess.TimeoutExpired) as error: | |
| raise ReleaseError("committed_source_bytes_unavailable") from error | |
| if result.returncode != 0 or len(result.stdout) > 32 * 1024 * 1024: | |
| raise ReleaseError("committed_source_bytes_unavailable") | |
| return result.stdout | |
| def _bind_git_source(source: Path, revision: str, selected_paths: list[str]) -> tuple[str, str]: | |
| root = _git(source, "rev-parse", "--show-toplevel") | |
| if os.path.normcase(str(Path(root).resolve())) != os.path.normcase(str(source.resolve())): | |
| raise ReleaseError("source_must_be_git_checkout_root") | |
| head = _git(source, "rev-parse", "--verify", "HEAD") | |
| tree = _git(source, "rev-parse", "--verify", "HEAD^{tree}") | |
| if head != revision or not re.fullmatch(r"[0-9a-f]{40}", head) or not re.fullmatch(r"[0-9a-f]{40}", tree): | |
| raise ReleaseError("release_revision_does_not_match_git_head") | |
| if _git(source, "status", "--porcelain=v1", "--untracked-files=no"): | |
| raise ReleaseError("tracked_source_checkout_must_be_clean") | |
| for relative in selected_paths: | |
| # Frontend artifacts are rebuilt from the exact commit below. They are | |
| # deliberately ignored by Git so generated bytes cannot masquerade as | |
| # reviewed source. | |
| if relative.startswith(FRONTEND_DIST_PREFIX): | |
| continue | |
| try: | |
| _git(source, "ls-files", "--error-unmatch", "--", relative) | |
| except ReleaseError as error: | |
| raise ReleaseError("selected_source_not_tracked_at_revision") from error | |
| return head, tree | |
| def _frontend_dist_inventory(source: Path) -> list[str]: | |
| root = source / "aureon-os-upstream/frontend/dist" | |
| if root.is_symlink() or not root.is_dir(): | |
| raise ReleaseError("frontend_dist_root_required") | |
| paths: list[str] = [] | |
| for path in root.rglob("*"): | |
| if path.is_symlink(): | |
| raise ReleaseError("frontend_dist_link_forbidden") | |
| if path.is_file(): | |
| paths.append(path.relative_to(source).as_posix()) | |
| return sorted(paths) | |
| def _operator_package_git_inventory(source: Path, revision: str) -> list[str]: | |
| """Read package paths from the immutable commit tree, never the worktree.""" | |
| output = _git( | |
| source, | |
| "ls-tree", | |
| "-r", | |
| "--name-only", | |
| revision, | |
| "--", | |
| OPERATOR_PACKAGE_PREFIX.rstrip("/"), | |
| ) | |
| return output.splitlines() | |
| def _git_archive(source: Path, revision: str, prefix: str) -> bytes: | |
| try: | |
| result = subprocess.run( | |
| ["git", "-C", str(source), "archive", "--format=tar", revision, "--", prefix], | |
| capture_output=True, | |
| timeout=120, | |
| check=False, | |
| ) | |
| except (OSError, subprocess.TimeoutExpired) as error: | |
| raise ReleaseError("frontend_commit_archive_unavailable") from error | |
| if result.returncode != 0 or not result.stdout: | |
| raise ReleaseError("frontend_commit_archive_unavailable") | |
| return result.stdout | |
| def _extract_regular_archive(raw: bytes, destination: Path, prefix: str) -> None: | |
| """Extract a Git archive without permitting links or path traversal.""" | |
| try: | |
| with tarfile.open(fileobj=io.BytesIO(raw), mode="r:") as archive: | |
| for member in archive.getmembers(): | |
| name = PurePosixPath(member.name) | |
| normalized = str(name) | |
| is_prefix_directory = member.isdir() and ( | |
| normalized == prefix or prefix.startswith(normalized + "/") | |
| ) | |
| if ( | |
| name.is_absolute() | |
| or ".." in name.parts | |
| or ( | |
| not normalized.startswith(prefix + "/") | |
| and not is_prefix_directory | |
| ) | |
| or member.issym() | |
| or member.islnk() | |
| or member.isdev() | |
| ): | |
| raise ReleaseError("frontend_commit_archive_invalid") | |
| target = destination.joinpath(*name.parts) | |
| if member.isdir(): | |
| target.mkdir(parents=True, exist_ok=True) | |
| continue | |
| if not member.isfile(): | |
| raise ReleaseError("frontend_commit_archive_invalid") | |
| target.parent.mkdir(parents=True, exist_ok=True) | |
| handle = archive.extractfile(member) | |
| if handle is None: | |
| raise ReleaseError("frontend_commit_archive_invalid") | |
| data = handle.read(MAX_RELEASE_BYTES + 1) | |
| if len(data) > MAX_RELEASE_BYTES: | |
| raise ReleaseError("frontend_commit_archive_invalid") | |
| target.write_bytes(data) | |
| except (tarfile.TarError, OSError) as error: | |
| raise ReleaseError("frontend_commit_archive_invalid") from error | |
| def _validate_frontend_environment(value: dict[str, str] | None) -> dict[str, str]: | |
| if not isinstance(value, dict) or set(value) - FRONTEND_ALLOWED_ENV: | |
| raise ReleaseError("frontend_public_build_environment_invalid") | |
| if not FRONTEND_REQUIRED_ENV.issubset(value) or value.get("VITE_REQUIRE_AUTH") != "1": | |
| raise ReleaseError("frontend_authenticated_build_environment_required") | |
| if re.fullmatch(r"https://[a-z0-9-]+\.supabase\.co", value.get("VITE_SUPABASE_URL", "")) is None: | |
| raise ReleaseError("frontend_supabase_url_invalid") | |
| key = value.get("VITE_SUPABASE_PUBLISHABLE_KEY", "") | |
| if not isinstance(key, str) or not 20 <= len(key) <= 4096 or re.search(r"\s", key): | |
| raise ReleaseError("frontend_supabase_publishable_key_invalid") | |
| for name, item in value.items(): | |
| if not isinstance(name, str) or not isinstance(item, str) or len(item) > 8192 or "\x00" in item: | |
| raise ReleaseError("frontend_public_build_environment_invalid") | |
| if any(marker in name for marker in ("SECRET", "PASSWORD", "PRIVATE", "SERVICE_ROLE")): | |
| raise ReleaseError("frontend_secret_build_environment_forbidden") | |
| return dict(sorted(value.items())) | |
| def _frontend_toolchain_executables( | |
| *, platform: str | None = None, resolver=None | |
| ) -> tuple[str, str]: | |
| lookup = shutil.which if resolver is None else resolver | |
| npm_name = "npm.cmd" if (os.name if platform is None else platform) == "nt" else "npm" | |
| npm = lookup(npm_name) | |
| node = lookup("node") | |
| if npm is None or node is None: | |
| raise ReleaseError("frontend_pinned_builder_unavailable") | |
| return npm, node | |
| def _default_frontend_builder( | |
| source: Path, revision: str, environment: dict[str, str] | |
| ) -> tuple[dict[str, bytes], dict[str, str]]: | |
| """Rebuild dist from a clean Git archive and a lockfile-only install.""" | |
| npm, node = _frontend_toolchain_executables() | |
| raw = _git_archive(source, revision, FRONTEND_SOURCE_ROOT) | |
| with tempfile.TemporaryDirectory(prefix="aureon-frontend-build-") as temporary: | |
| root = Path(temporary) | |
| _extract_regular_archive(raw, root, FRONTEND_SOURCE_ROOT) | |
| frontend = root / FRONTEND_SOURCE_ROOT | |
| if not (frontend / "package-lock.json").is_file(): | |
| raise ReleaseError("frontend_lockfile_required") | |
| clean_env = { | |
| key: os.environ[key] | |
| for key in ("PATH", "SystemRoot", "SYSTEMROOT", "WINDIR", "COMSPEC", "PATHEXT") | |
| if key in os.environ | |
| } | |
| clean_env.update( | |
| { | |
| "CI": "1", | |
| "TZ": "UTC", | |
| "NPM_CONFIG_AUDIT": "false", | |
| "NPM_CONFIG_FUND": "false", | |
| "NPM_CONFIG_UPDATE_NOTIFIER": "false", | |
| "npm_config_cache": str(root / "npm-cache"), | |
| **environment, | |
| } | |
| ) | |
| for command in ( | |
| [npm, "ci", "--ignore-scripts", "--no-audit", "--no-fund"], | |
| [npm, "run", "build"], | |
| ): | |
| try: | |
| result = subprocess.run( | |
| command, | |
| cwd=frontend, | |
| env=clean_env, | |
| capture_output=True, | |
| timeout=900, | |
| check=False, | |
| ) | |
| except (OSError, subprocess.TimeoutExpired) as error: | |
| raise ReleaseError("frontend_reproducible_build_failed") from error | |
| if result.returncode != 0: | |
| raise ReleaseError("frontend_reproducible_build_failed") | |
| files: dict[str, bytes] = {} | |
| dist = frontend / "dist" | |
| if dist.is_symlink() or not dist.is_dir(): | |
| raise ReleaseError("frontend_reproducible_build_failed") | |
| for path in sorted(dist.rglob("*")): | |
| if path.is_symlink(): | |
| raise ReleaseError("frontend_dist_link_forbidden") | |
| if path.is_file(): | |
| relative = FRONTEND_DIST_PREFIX + path.relative_to(dist).as_posix() | |
| files[relative] = read_bytes(path) | |
| try: | |
| node_version = subprocess.run( | |
| [node, "--version"], capture_output=True, text=True, timeout=10, check=True | |
| ).stdout.strip() | |
| npm_version = subprocess.run( | |
| [npm, "--version"], capture_output=True, text=True, timeout=10, check=True | |
| ).stdout.strip() | |
| except (OSError, subprocess.SubprocessError) as error: | |
| raise ReleaseError("frontend_pinned_builder_unavailable") from error | |
| return files, {"node": node_version, "npm": npm_version} | |
| def _vm_python_runtime_manifest( | |
| package_tree: list[str], rows: list[dict], *, source_git_tree: str | |
| ) -> dict: | |
| try: | |
| required = frozenset(operator_package_runtime_sources(package_tree)) | OPERATOR_COMPANION_SOURCES | |
| except OperatorRuntimeClosureError as error: | |
| raise ReleaseError(str(error)) from error | |
| row_by_path = {row["path"]: row for row in rows} | |
| if not required.issubset(row_by_path): | |
| raise ReleaseError("complete_vm_python_runtime_sources_required") | |
| selected = [row_by_path[path] for path in sorted(required)] | |
| return { | |
| "schema": "aureon.vm-python-runtime-closure.v1", | |
| "source_git_tree": source_git_tree, | |
| "sources": sorted(required), | |
| "files": len(required), | |
| "payload_sha256": manifest_payload_sha256(selected), | |
| } | |
| def _component_manifest(rows: list[dict], required: frozenset[str], reason: str) -> dict: | |
| row_by_path = {row["path"]: row for row in rows} | |
| if not required.issubset(row_by_path): | |
| raise ReleaseError(reason) | |
| selected = [row_by_path[path] for path in sorted(required)] | |
| return { | |
| "sources": sorted(required), | |
| "files": len(required), | |
| "payload_sha256": manifest_payload_sha256(selected), | |
| } | |
| def _parse_receipt_time(value: object) -> float: | |
| if not isinstance(value, str) or len(value) > 64: | |
| raise ReleaseError("bridge_certificate_fingerprint_receipt_invalid") | |
| try: | |
| return datetime.fromisoformat(value.replace("Z", "+00:00")).astimezone(UTC).timestamp() | |
| except (ValueError, TypeError, OverflowError) as error: | |
| raise ReleaseError("bridge_certificate_fingerprint_receipt_invalid") from error | |
| def _certificate_receipt( | |
| path: Path | None, | |
| expected_sha256: str, | |
| revision: str, | |
| *, | |
| now: float | None = None, | |
| ) -> dict: | |
| if path is None or re.fullmatch(r"[0-9a-f]{64}", expected_sha256 or "") is None: | |
| raise ReleaseError("bridge_certificate_fingerprint_receipt_required") | |
| raw = read_bytes(path, 1024 * 1024) | |
| if digest(raw) != expected_sha256: | |
| raise ReleaseError("bridge_certificate_fingerprint_receipt_hash_mismatch") | |
| try: | |
| value = json.loads(raw) | |
| except (ValueError, TypeError, UnicodeError) as error: | |
| raise ReleaseError("bridge_certificate_fingerprint_receipt_invalid") from error | |
| recorded = _parse_receipt_time(value.get("recorded_at")) | |
| not_before = _parse_receipt_time(value.get("not_before")) | |
| not_after = _parse_receipt_time(value.get("not_after")) | |
| current = time.time() if now is None else float(now) | |
| if recorded > current + 300 or current - recorded > 21_600: | |
| raise ReleaseError("bridge_certificate_fingerprint_receipt_not_fresh") | |
| if ( | |
| value.get("schema") != BRIDGE_CERTIFICATE_RECEIPT_SCHEMA | |
| or value.get("status") != "passed" | |
| or value.get("project") != "aureon-trading" | |
| or re.fullmatch(r"[a-z0-9-]+-[a-z0-9]+[0-9]-[a-z]", str(value.get("zone") or "")) is None | |
| or re.fullmatch(r"[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?", str(value.get("instance") or "")) is None | |
| or value.get("source_revision") != revision | |
| or value.get("certificate_path") != "/etc/aureon-public/bridge-cert.pem" | |
| or value.get("algorithm") != "x509-der-sha256" | |
| or re.fullmatch(r"[0-9a-f]{64}", str(value.get("certificate_sha256") or "")) is None | |
| or value.get("required_ip_sans") != ["10.40.0.2"] | |
| or value.get("minimum_remaining_validity_seconds") != 86_400 | |
| or isinstance(value.get("minimum_remaining_validity_seconds"), bool) | |
| or not_before > recorded | |
| or recorded > not_after | |
| or not_before > current | |
| or not_after - current < 86_400 | |
| or value.get("private_key_included") is not False | |
| or value.get("secret_values_recorded") is not False | |
| ): | |
| raise ReleaseError("bridge_certificate_fingerprint_receipt_invalid") | |
| return { | |
| "schema": value["schema"], | |
| "sha256": expected_sha256, | |
| "recorded_at": value["recorded_at"], | |
| "project": value["project"], | |
| "zone": value["zone"], | |
| "instance": value["instance"], | |
| "fingerprint_sha256": value["certificate_sha256"], | |
| "required_ip_sans": value["required_ip_sans"], | |
| "not_before": value["not_before"], | |
| "not_after": value["not_after"], | |
| "minimum_remaining_validity_seconds": value[ | |
| "minimum_remaining_validity_seconds" | |
| ], | |
| } | |
| def build( | |
| source: Path, | |
| selected: list, | |
| destination: Path, | |
| revision: str, | |
| release_id: str, | |
| *, | |
| frontend_build_environment: dict[str, str] | None = None, | |
| frontend_builder=None, | |
| bridge_certificate_receipt: Path | None = None, | |
| bridge_certificate_receipt_sha256: str = "", | |
| ) -> dict: | |
| if not re.fullmatch(r"[0-9a-f]{40}", revision) or not re.fullmatch(r"[0-9]{8}T[0-9]{6}Z-[0-9a-f]{8,16}", release_id): | |
| raise ReleaseError("exact_revision_and_dated_release_required") | |
| if not isinstance(selected, list) or not selected or len(selected) > 20000: | |
| raise ReleaseError("explicit_bounded_source_list_required") | |
| if destination.exists() or destination.is_symlink(): | |
| raise ReleaseError("bundle_destination_must_be_new") | |
| if destination.resolve().is_relative_to(source.resolve()): | |
| raise ReleaseError("bundle_must_be_outside_source_checkout") | |
| seen, selected_paths, selected_items = set(), [], [] | |
| for item in selected: | |
| relative = source_path(item if isinstance(item, str) else item.get("path")) | |
| if relative.casefold() in seen: | |
| raise ReleaseError("duplicate_source_selection") | |
| seen.add(relative.casefold()) | |
| selected_paths.append(relative) | |
| selected_items.append((item, relative)) | |
| git_revision, git_tree = _bind_git_source(source, revision, selected_paths) | |
| rows, payload, total = [], [], 0 | |
| for item, relative in selected_items: | |
| data = ( | |
| read_bytes(confined(source, relative)) | |
| if relative.startswith(FRONTEND_DIST_PREFIX) | |
| else _git_blob(source, revision, relative) | |
| ) | |
| hashed = digest(data) | |
| if isinstance(item, dict) and item.get("sha256") != hashed: | |
| raise ReleaseError("reviewed_source_hash_changed") | |
| total += len(data) | |
| if total > MAX_RELEASE_BYTES: | |
| raise ReleaseError("release_size_limit") | |
| rows.append({"path": relative, "sha256": hashed, "size": len(data)}) | |
| payload.append((relative, data)) | |
| package_tree = _operator_package_git_inventory(source, revision) | |
| operator_runtime = None | |
| if operator_runtime_requested(selected_paths): | |
| try: | |
| operator_runtime = build_operator_runtime_manifest( | |
| package_tree, | |
| rows, | |
| source_git_tree=git_tree, | |
| ) | |
| except OperatorRuntimeClosureError as error: | |
| raise ReleaseError(str(error)) from error | |
| vm_python_runtime = None | |
| if operator_runtime is not None or set(selected_paths) & VM_RUNTIME_TRIGGER_SOURCES: | |
| vm_python_runtime = _vm_python_runtime_manifest( | |
| package_tree, rows, source_git_tree=git_tree | |
| ) | |
| selected_dist = sorted(path for path in selected_paths if path.startswith(FRONTEND_DIST_PREFIX)) | |
| frontend_dist = None | |
| if selected_dist: | |
| expected_dist = _frontend_dist_inventory(source) | |
| if FRONTEND_DIST_INDEX not in selected_dist or selected_dist != expected_dist: | |
| raise ReleaseError("complete_frontend_dist_inventory_required") | |
| public_environment = _validate_frontend_environment(frontend_build_environment) | |
| frontend_rows = [row for row in rows if row["path"].startswith(FRONTEND_DIST_PREFIX)] | |
| rebuilt, toolchain = (frontend_builder or _default_frontend_builder)( | |
| source, revision, public_environment | |
| ) | |
| actual = { | |
| relative: read_bytes(confined(source, relative)) for relative in expected_dist | |
| } | |
| if set(rebuilt) != set(actual) or any( | |
| digest(rebuilt[path]) != digest(actual[path]) for path in actual | |
| ): | |
| raise ReleaseError("frontend_dist_not_reproducible_from_commit") | |
| frontend_dist = { | |
| "schema": FRONTEND_BUILD_SCHEMA, | |
| "entrypoint": FRONTEND_DIST_INDEX, | |
| "files": len(frontend_rows), | |
| "sha256": manifest_payload_sha256(frontend_rows), | |
| "source_git_revision": git_revision, | |
| "source_git_tree": git_tree, | |
| "recipe": list(FRONTEND_BUILD_RECIPE), | |
| "environment_keys": sorted(public_environment), | |
| "environment_sha256": digest( | |
| json.dumps(public_environment, sort_keys=True, separators=(",", ":")).encode() | |
| ), | |
| "toolchain": toolchain, | |
| } | |
| broker = None | |
| if set(selected_paths) & {"deployment/google-cloud/aureon-cloud-token-broker.service", "deployment/google-cloud/vm_token_broker.py"}: | |
| broker = _component_manifest( | |
| rows, TOKEN_BROKER_SOURCES, "complete_token_broker_sources_required" | |
| ) | |
| broker.update( | |
| { | |
| "schema": "aureon.vm-token-broker-release.v1", | |
| "source_git_tree": git_tree, | |
| } | |
| ) | |
| public_bridge = None | |
| if set(selected_paths) & PUBLIC_BRIDGE_SOURCES: | |
| certificate_receipt = _certificate_receipt( | |
| bridge_certificate_receipt, | |
| bridge_certificate_receipt_sha256, | |
| revision, | |
| ) | |
| public_bridge = _component_manifest( | |
| rows, PUBLIC_BRIDGE_SOURCES, "complete_public_bridge_sources_required" | |
| ) | |
| row_by_path = {row["path"]: row for row in rows} | |
| public_bridge.update( | |
| { | |
| "schema": "aureon.vm-public-bridge-release.v1", | |
| "revision": revision, | |
| "source_git_tree": git_tree, | |
| "source_sha256": row_by_path[PUBLIC_BRIDGE_SOURCE]["sha256"], | |
| "unit_sha256": row_by_path[PUBLIC_BRIDGE_UNIT_SOURCE]["sha256"], | |
| "certificate": { | |
| "path": "/etc/aureon-public/bridge-cert.pem", | |
| "algorithm": "x509-der-sha256", | |
| "sha256": certificate_receipt["fingerprint_sha256"], | |
| "receipt": certificate_receipt, | |
| }, | |
| } | |
| ) | |
| destination.mkdir(parents=True, mode=0o700) | |
| (destination / "files").mkdir() | |
| for relative, data in payload: | |
| durable_write(confined(destination / "files", relative), data, 0o644) | |
| manifest = {"schema": SCHEMA, "revision": revision, "release_id": release_id, | |
| "source_git_revision": git_revision, "source_git_tree": git_tree, | |
| "payload_sha256": manifest_payload_sha256(rows), | |
| "frontend_dist": frontend_dist, | |
| "operator_runtime": operator_runtime, | |
| "vm_python_runtime": vm_python_runtime, | |
| "token_broker": broker, | |
| "public_bridge": public_bridge, | |
| "files": rows, "bytes": total, | |
| "contains_runtime_state": False} | |
| durable_write(destination / "manifest.json", (json.dumps(manifest, sort_keys=True, indent=2) + "\n").encode()) | |
| return manifest | |
| def main(): | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--source", required=True, type=Path) | |
| parser.add_argument("--paths", required=True, type=Path) | |
| parser.add_argument("--destination", required=True, type=Path) | |
| parser.add_argument("--revision", required=True) | |
| parser.add_argument("--release-id", required=True) | |
| parser.add_argument("--frontend-build-env", type=Path) | |
| parser.add_argument("--bridge-certificate-receipt", type=Path) | |
| parser.add_argument("--bridge-certificate-receipt-sha256", default="") | |
| args = parser.parse_args() | |
| selected = json.loads(read_bytes(args.paths, 4 * 1024 * 1024)) | |
| frontend_environment = None | |
| if args.frontend_build_env is not None: | |
| frontend_environment = json.loads( | |
| read_bytes(args.frontend_build_env, 64 * 1024) | |
| ) | |
| manifest = build( | |
| args.source, | |
| selected, | |
| args.destination, | |
| args.revision, | |
| args.release_id, | |
| frontend_build_environment=frontend_environment, | |
| bridge_certificate_receipt=args.bridge_certificate_receipt, | |
| bridge_certificate_receipt_sha256=args.bridge_certificate_receipt_sha256, | |
| ) | |
| print(json.dumps({"files": len(manifest["files"]), "bytes": manifest["bytes"], "revision": manifest["revision"]})) | |
| if __name__ == "__main__": | |
| main() | |