| 421 | |
| 422 | |
| 423 | def download_and_render_compose( |
| 424 | workdir: Path, tag: str, hash_key: str, admin_password: str, |
| 425 | initial_api_key: str, http_port: int, grpc_port: int, |
| 426 | ) -> None: |
| 427 | url = COMPOSE_TEMPLATE_URL.format(repo=GITHUB_REPO, tag=tag) |
| 428 | info(f"Downloading {url}") |
| 429 | try: |
| 430 | req = urllib.request.Request(url) |
| 431 | with urllib.request.urlopen(req) as resp: |
| 432 | template = resp.read().decode() |
| 433 | except (urllib.error.HTTPError, urllib.error.URLError) as exc: |
| 434 | fatal("download", f"Failed to download compose template: {exc}") |
| 435 | |
| 436 | for ph in PLACEHOLDERS: |
| 437 | if ph not in template: |
| 438 | fatal("template", f"Placeholder {ph} not found in compose template.") |
| 439 | |
| 440 | rendered = template |
| 441 | rendered = rendered.replace("{{IMAGE_TAG}}", tag) |
| 442 | rendered = rendered.replace("{{HASH_KEY}}", hash_key) |
| 443 | rendered = rendered.replace("{{ADMIN_PASSWORD}}", admin_password) |
| 444 | rendered = rendered.replace("{{INITIAL_API_KEY}}", initial_api_key) |
| 445 | rendered = rendered.replace("{{HTTP_PORT}}", str(http_port)) |
| 446 | rendered = rendered.replace("{{GRPC_PORT}}", str(grpc_port)) |
| 447 | |
| 448 | leftover = re.findall(r"\{\{.*?\}\}", rendered) |
| 449 | if leftover: |
| 450 | fatal("template", f"Unreplaced placeholders remain: {leftover}") |
| 451 | |
| 452 | compose_path = workdir / "docker-compose.yml" |
| 453 | compose_path.write_text(rendered) |
| 454 | info(f"Compose file written to {compose_path}") |
| 455 | |
| 456 | |
| 457 | def wait_console_ready(http_port: int, admin_password: str) -> None: |