"""Guard and apply worker settings to the existing isolated local development stack.""" import argparse import hashlib import json import os from pathlib import Path import subprocess ROOT = Path(__file__).resolve().parent.parent SIDECAR = "sha256:8a29cb90fcc2817b0d784826d55a172c943f5ad7e1b6105e690a2d79ee15fe87" def capture(arguments, environment): return subprocess.check_output(["rtk", "proxy", *arguments], env=environment, text=True) def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--checkout", type=Path, required=True) parser.add_argument("--bart-artifact", type=Path, required=True) parser.add_argument("--output", type=Path, required=True) parser.add_argument("--apply", action="store_true") args = parser.parse_args() checkout = args.checkout.resolve() artifact = args.bart_artifact.resolve() environment = {**os.environ, "JOBTRACKER_DEV_BART_ARTIFACT": str(artifact)} manifest = json.loads((ROOT / "docs/redesign/phase-9/p9-sec-02/full-model/ARTIFACTS.json").read_text())["weights-only"] for row in manifest["files"]: path = artifact / row["file"] assert path.is_file() and path.stat().st_size == row["bytes"], "Local model file missing or wrong size" with path.open("rb") as stream: assert hashlib.file_digest(stream, "sha256").hexdigest() == row["sha256"], "Local model integrity mismatch" assert json.loads((ROOT / ".artifacts/pre-phase10-ux/local-ai/verification.json").read_text())["status"] == "PASS" compose = ["docker", "compose", "--project-directory", str(checkout), "--env-file", str(checkout / ".env.dev"), "-f", str(checkout / "docker-compose.dev.yml"), "-f", str(ROOT / "dev/local-workers.override.yml")] config = json.loads(capture([*compose, "config", "--format", "json"], environment)) assert config["name"] == "jobjakt-dev", "Only the local development project is permitted" backend = config["services"]["backend"] sidecar = config["services"]["ai-service"] settings = backend["environment"] assert settings["ASPNETCORE_ENVIRONMENT"] == "Development" connection = settings["ConnectionStrings__JobTracker"].lower() assert "server=mariadb;" in connection and "database=jobtracker_dev;" in connection assert settings["Ai__ExternalProcessingEnabled"].lower() == "false" assert settings["Email__SmtpHost"] == "mailpit" and settings["Email__SmtpPort"] == "1025" assert not settings["Email__SmtpUser"] and not settings["Email__SmtpPassword"] assert all(port.get("host_ip") == "127.0.0.1" for port in backend.get("ports", [])) assert sidecar["environment"]["HF_HUB_OFFLINE"] == "1" assert sidecar["environment"]["TRANSFORMERS_OFFLINE"] == "1" assert sidecar["environment"]["EXTERNAL_AI_ENABLED"].lower() == "false" assert sidecar["environment"]["OLLAMA_BASE_URL"] == "http://jobjakt-dev-ollama:11434" assert any(volume.get("source") == str(artifact) and volume.get("read_only") for volume in sidecar["volumes"]) assert json.loads(capture(["docker", "image", "inspect", sidecar["image"]], environment))[0]["Id"] == SIDECAR current = json.loads(capture(["docker", "inspect", "jobjakt-dev-backend-1"], environment))[0] assert current["Config"]["Labels"]["com.docker.compose.project"] == "jobjakt-dev" assert json.loads(capture(["docker", "image", "inspect", backend["image"]], environment))[0]["Id"] == current["Image"], "This operation changes worker configuration, not backend code" record = {"project": config["name"], "verifiedBartRevision": manifest["sourceRevision"], "sidecarImage": SIDECAR, "enabledWorkers": {key: value for key, value in settings.items() if key.startswith("Workers__")}, "emailSink": "Mailpit", "externalAI": False, "applied": False} args.output.parent.mkdir(parents=True, exist_ok=True) if args.apply: with args.output.with_suffix(".log").open("w", encoding="utf-8") as log: result = subprocess.run(["rtk", "proxy", *compose, "up", "-d", "--no-deps", "--no-build", "--pull", "never", "--wait", "--wait-timeout", "120", "backend", "ai-service"], env=environment, stdout=log, stderr=subprocess.STDOUT) record["exitCode"] = result.returncode record["applied"] = result.returncode == 0 args.output.write_text(json.dumps(record, indent=2) + "\n", encoding="utf-8") print(json.dumps(record)) if args.apply: raise SystemExit(record["exitCode"]) if __name__ == "__main__": main()