feat: add durable Core Hub absorption runtime
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / pytest-smoke (push) Failing after 2s

This commit is contained in:
tegwick 2026-08-21 16:16:42 +02:00
parent 7e1ec03f0c
commit 8ab1d0c09a
20 changed files with 2423 additions and 15 deletions

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import argparse
import json
from importlib.resources import files
from pathlib import Path
from typing import Sequence
from hub_core.mcp import HubCoreMCPServer
@ -28,6 +29,21 @@ def build_parser(settings: RuntimeSettings | None = None) -> argparse.ArgumentPa
migrate.add_argument("revision", nargs="?", default="head")
migrate.add_argument("--database-url", default=resolved.database_url)
migration = commands.add_parser("migration", help="Export, validate, or import migration bundles")
migration_commands = migration.add_subparsers(dest="migration_command", required=True)
migration_validate = migration_commands.add_parser("validate")
migration_validate.add_argument("bundle", type=Path)
migration_validate.add_argument("--output", type=Path)
migration_import = migration_commands.add_parser("import")
migration_import.add_argument("bundle", type=Path)
migration_import.add_argument("--database-url", default=resolved.database_url)
migration_import.add_argument("--dry-run", action="store_true")
migration_import.add_argument("--output", type=Path)
migration_export = migration_commands.add_parser("export")
migration_export.add_argument("--database-url", default=resolved.database_url)
migration_export.add_argument("--source-revision")
migration_export.add_argument("--output", type=Path, required=True)
conformance = commands.add_parser(
"conformance",
help="Run the implemented Tier 2/3 profile against an HTTP runtime",
@ -53,6 +69,8 @@ def main(argv: Sequence[str] | None = None) -> int:
raise SystemExit("hub-core migrate requires --database-url or HUB_CORE_DATABASE_URL")
_run_migrations(args.database_url, args.revision)
return 0
if args.command == "migration":
return _run_migration(args)
if args.command == "conformance":
return _run_conformance(args.base_url, args.timeout, args.as_json)
raise AssertionError(f"Unhandled command {args.command}")
@ -96,5 +114,38 @@ def _run_conformance(base_url: str, timeout: float, as_json: bool) -> int:
return 0 if report.passed else 1
def _run_migration(args: argparse.Namespace) -> int:
import asyncio
from hub_core.runtime.migration import (
export_bundle,
import_bundle,
load_bundle,
validate_bundle,
)
from hub_core.runtime.postgres_store import PostgresPortStore
if args.migration_command == "validate":
report = validate_bundle(load_bundle(args.bundle))
else:
if not args.database_url:
raise SystemExit("migration command requires --database-url or HUB_CORE_DATABASE_URL")
store = PostgresPortStore.from_url(args.database_url)
async def run() -> dict:
try:
if args.migration_command == "import":
return await import_bundle(store, load_bundle(args.bundle), dry_run=args.dry_run)
return await export_bundle(store, source_revision=args.source_revision)
finally:
await store.aclose()
report = asyncio.run(run())
rendered = json.dumps(report, indent=2, sort_keys=True)
if args.output:
args.output.write_text(rendered + "\n")
else:
print(rendered)
return 0 if report.get("ok", True) else 1
def _sync_database_url(database_url: str) -> str:
return database_url.replace("postgresql+asyncpg://", "postgresql+psycopg2://")