#!/usr/bin/env python3 """One-time importer: copy XVI/XVII challenge sources into the platform services/ tree, with EOL base-image fixes and a canonical docker-compose.yml (per-challenge template) that the compose generator can render per team. Run after cloning the upstream repos: python3 import_new_challenges.py For each imported challenge it writes services//: - source files (Dockerfile, src, start.sh, requirements, db dumps...) - docker-compose.yml (canonical template: single main service + sidecars) - apt-insecure.conf (2026-clock GPG fix) """ import re import shutil import sys from pathlib import Path BASE = Path("/opt/gemastik18-final") SVC = BASE / "services" # ---------------------------------------------------------------- helpers def copy_tree(src: Path, dst: Path, ignore=None): if dst.exists(): shutil.rmtree(dst) shutil.copytree(src, dst, ignore=ignore) def add_apt_insecure(d: Path): conf = "Acquire::AllowInsecureRepositories \"true\";\nAcquire::AllowDowngradeToInsecureRepositories \"true\";\nApt::Get::AllowUnauthenticated \"true\";\n" (d / "apt-insecure.conf").write_text(conf) def fix_base_image(d: Path, old: str, new: str): """Swap the FROM line in a Dockerfile (EOL base -> supported).""" df = d / "Dockerfile" if not df.exists(): return False t = df.read_text() if old in t: df.write_text(t.replace(old, new, 1)) print(f" [fix base] {d.name}: {old} -> {new}") return True return False DROP_FROM = [ (r"public\.ecr\.aws/docker/library/(python:3\.11-slim-buster|python:3\.11-slim-bullseye)\b", "python:3.11-slim-bookworm"), (r"public\.ecr\.aws/docker/library/ruby:2\.7\.2\b", "ruby:3.2-slim-bookworm"), (r"public\.ecr\.aws/docker/library/php:8\.0-apache\b", "php:8.2-apache-bookworm"), (r"public\.ecr\.aws/docker/library/golang:bullseye\b", "golang:1.22-bookworm"), (r"public\.ecr\.aws/docker/library/ubuntu:20\.04\b", "ubuntu:24.04"), (r"public\.ecr\.aws/docker/library/ubuntu:22\.04\b", "ubuntu:24.04"), (r"public\.ecr\.aws/docker/library/node(:[0-9]+)?\b", "node:20-slim-bookworm"), (r"public\.ecr\.aws/docker/library/python:3\.10\.6\b", "python:3.10-slim-bookworm"), ] def fix_dockerfile(d: Path): df = d / "Dockerfile" if not df.exists(): return t = df.read_text() for pat, new in DROP_FROM: t2 = re.sub(pat, new, t) if t2 != t: print(f" [fix base] {d.name}: {pat} -> {new}") t = t2 # apt-insecure for every apt-get run if "apt-get" in t and "99gemastik-insecure" not in t: t = t.replace( "RUN apt-get update", "COPY apt-insecure.conf /etc/apt/apt.conf.d/99gemastik-insecure\nRUN apt-get -o Acquire::AllowInsecureRepositories=true update", 1) t = t.replace( "RUN apt-get install", "RUN apt-get -y --allow-unauthenticated install", 1) # ensure openssh + ctfuser-ish (the standard template) df.write_text(t) # ---------------------------------------------------------------- import def import_xvi(src: Path): print(f"=== Importing XVI from {src} ===") services = src / "services" for d in sorted(services.iterdir()): if not d.is_dir() or d.name == ".git": continue name = d.name dst = SVC / name print(f" - {name}") copy_tree(d, dst, ignore=shutil.ignore_patterns("__pycache__", ".git")) add_apt_insecure(dst) fix_dockerfile(dst) (dst / "docker-compose.yml").unlink(missing_ok=True) # special: gemas-notes, gemas-fetcher need their src subdirs — already copied whole dir. def import_xvii(src: Path): print(f"=== Importing XVII from {src} ===") for d in sorted(src.iterdir()): if not d.is_dir() or d.name.startswith("."): continue name = d.name dst = SVC / name print(f" - {name}") copy_tree(d, dst, ignore=shutil.ignore_patterns("__pycache__", ".git", "docker-compose.yml", "README.md", "test")) add_apt_insecure(dst) fix_dockerfile(dst) (dst / "docker-compose.yml").unlink(missing_ok=True) if name == "tempest-poc": # compose is frontend+backend split; keep both Dockerfiles for sub in ("backend", "frontend"): subd = dst / sub if subd.exists(): if (subd / "Dockerfile").exists(): fix_dockerfile(subd) add_apt_insecure(subd) if __name__ == "__main__": xvi = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("/opt/gemastik-xvi-final") xvii = Path(sys.argv[2]) if len(sys.argv) > 2 else Path("/opt/gemastik-xvii-final") import_xvi(xvi) import_xvii(xvii) print("Done. Next: write canonical docker-compose.yml templates per challenge.")