mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-13 08:14:58 -05:00
155 lines
5.7 KiB
Python
155 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Add cache configuration to a resolved docker-compose file for all services
|
|
that have a build key, and ensure image names match what docker compose expects.
|
|
"""
|
|
|
|
import argparse
|
|
|
|
import yaml
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Add cache config to a resolved compose file"
|
|
)
|
|
parser.add_argument(
|
|
"--source",
|
|
required=True,
|
|
help="Source compose file to read (should be output of `docker compose config`)",
|
|
)
|
|
parser.add_argument(
|
|
"--cache-from",
|
|
default="type=gha",
|
|
help="Cache source configuration",
|
|
)
|
|
parser.add_argument(
|
|
"--cache-to",
|
|
default="type=gha,mode=max",
|
|
help="Cache destination configuration",
|
|
)
|
|
parser.add_argument(
|
|
"--backend-scope",
|
|
default="",
|
|
help="GHA cache scope for backend services (e.g., platform-backend-{hash})",
|
|
)
|
|
parser.add_argument(
|
|
"--frontend-scope",
|
|
default="",
|
|
help="GHA cache scope for frontend service (e.g., platform-frontend-{hash})",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
with open(args.source, "r") as f:
|
|
compose = yaml.safe_load(f)
|
|
|
|
# Get project name from compose file or default
|
|
project_name = compose.get("name", "autogpt_platform")
|
|
|
|
def get_image_name(dockerfile: str, target: str) -> str:
|
|
"""Generate image name based on Dockerfile folder and build target."""
|
|
dockerfile_parts = dockerfile.replace("\\", "/").split("/")
|
|
if len(dockerfile_parts) >= 2:
|
|
folder_name = dockerfile_parts[-2] # e.g., "backend" or "frontend"
|
|
else:
|
|
folder_name = "app"
|
|
return f"{project_name}-{folder_name}:{target}"
|
|
|
|
def get_build_key(dockerfile: str, target: str) -> str:
|
|
"""Generate a unique key for a Dockerfile+target combination."""
|
|
return f"{dockerfile}:{target}"
|
|
|
|
# First pass: collect all services with build configs and identify duplicates
|
|
# Track which (dockerfile, target) combinations we've seen
|
|
build_key_to_first_service: dict[str, str] = {}
|
|
services_to_build: list[str] = []
|
|
services_to_dedupe: list[str] = []
|
|
|
|
for service_name, service_config in compose.get("services", {}).items():
|
|
if "build" not in service_config:
|
|
continue
|
|
|
|
build_config = service_config["build"]
|
|
dockerfile = build_config.get("dockerfile", "Dockerfile")
|
|
target = build_config.get("target", "default")
|
|
build_key = get_build_key(dockerfile, target)
|
|
|
|
if build_key not in build_key_to_first_service:
|
|
# First service with this build config - it will do the actual build
|
|
build_key_to_first_service[build_key] = service_name
|
|
services_to_build.append(service_name)
|
|
else:
|
|
# Duplicate - will just use the image from the first service
|
|
services_to_dedupe.append(service_name)
|
|
|
|
# Second pass: configure builds and deduplicate
|
|
modified_services = []
|
|
for service_name, service_config in compose.get("services", {}).items():
|
|
if "build" not in service_config:
|
|
continue
|
|
|
|
build_config = service_config["build"]
|
|
dockerfile = build_config.get("dockerfile", "Dockerfile")
|
|
target = build_config.get("target", "latest")
|
|
image_name = get_image_name(dockerfile, target)
|
|
|
|
# Set image name for all services (needed for both builders and deduped)
|
|
service_config["image"] = image_name
|
|
|
|
if service_name in services_to_dedupe:
|
|
# Remove build config - this service will use the pre-built image
|
|
del service_config["build"]
|
|
continue
|
|
|
|
# This service will do the actual build - add cache config
|
|
cache_from = args.cache_from
|
|
cache_to = args.cache_to
|
|
|
|
# Determine scope based on Dockerfile path and target
|
|
# Each unique (dockerfile, target) combination gets its own cache scope
|
|
if "type=gha" in args.cache_from or "type=gha" in args.cache_to:
|
|
if "frontend" in dockerfile:
|
|
base_scope = args.frontend_scope
|
|
elif "backend" in dockerfile:
|
|
base_scope = args.backend_scope
|
|
else:
|
|
# Skip services that don't clearly match frontend/backend
|
|
continue
|
|
|
|
if base_scope:
|
|
# Append target to scope to differentiate e.g. migrate vs server
|
|
scope = f"{base_scope}-{target}"
|
|
if "type=gha" in args.cache_from:
|
|
cache_from = f"{args.cache_from},scope={scope}"
|
|
if "type=gha" in args.cache_to:
|
|
cache_to = f"{args.cache_to},scope={scope}"
|
|
|
|
build_config["cache_from"] = [cache_from]
|
|
build_config["cache_to"] = [cache_to]
|
|
modified_services.append(service_name)
|
|
|
|
# Write back to the same file
|
|
with open(args.source, "w") as f:
|
|
yaml.dump(compose, f, default_flow_style=False, sort_keys=False)
|
|
|
|
print(f"Added cache config to {len(modified_services)} services in {args.source}:")
|
|
for svc in modified_services:
|
|
svc_config = compose["services"][svc]
|
|
build_cfg = svc_config.get("build", {})
|
|
cache_from_val = build_cfg.get("cache_from", ["none"])[0]
|
|
cache_to_val = build_cfg.get("cache_to", ["none"])[0]
|
|
print(f" - {svc}")
|
|
print(f" image: {svc_config.get('image', 'N/A')}")
|
|
print(f" cache_from: {cache_from_val}")
|
|
print(f" cache_to: {cache_to_val}")
|
|
if services_to_dedupe:
|
|
print(
|
|
f"Deduplicated {len(services_to_dedupe)} services (will use pre-built images):"
|
|
)
|
|
for svc in services_to_dedupe:
|
|
print(f" - {svc} -> {compose['services'][svc].get('image', 'N/A')}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|