Source code for controller.commands.build

"""
Force building of one or more services docker images
"""

from typing import List, Set

import typer

from controller import COMPOSE_FILE, RED, log, print_and_exit
from controller.app import Application, Configuration
from controller.deploy.builds import (
    find_templates_build,
    get_dockerfile_base_image,
    get_non_redundant_services,
)
from controller.deploy.docker import Docker


[docs] @Application.app.command(help="Force building of one or more services docker images") def build( services: List[str] = typer.Argument( None, help="Services to be built", shell_complete=Application.autocomplete_service, ), core: bool = typer.Option( False, "--core", help="Include core images to the build list", show_default=False, ), force: bool = typer.Option( False, "--force", "-f", help="remove the cache to force the build", show_default=False, ), ) -> bool: Application.print_command( Application.serialize_parameter("--core", core, IF=core), Application.serialize_parameter("--force", force, IF=force), Application.serialize_parameter("", services), ) Application.get_controller().controller_init(services) docker = Docker() if docker.client.buildx.is_installed(): v = docker.client.buildx.version() log.debug("docker buildx is installed: {}", v) else: # pragma: no cover print_and_exit( "A mandatory dependency is missing: docker buildx not found" "\nInstallation guide: https://github.com/docker/buildx#binary-release" "\nor try the automated installation with {command}", command=RED("rapydo install buildx"), ) if Configuration.swarm_mode: docker.registry.ping() docker.registry.login() images: Set[str] = set() if core: log.debug("Forcing rebuild of core builds") # Create merged compose file with core files only docker = Docker(compose_files=Application.data.base_files) docker.compose.dump_config(Application.data.services, set_registry=False) log.debug("Compose configuration dumped on {}", COMPOSE_FILE) docker.client.buildx.bake( targets=Application.data.services, files=[COMPOSE_FILE], pull=True, load=True, cache=not force, ) log.info("Core images built") if Configuration.swarm_mode: log.warning("Local registry push is not implemented yet for core images") docker = Docker() docker.compose.dump_config(Application.data.services, set_registry=False) log.debug("Compose configuration dumped on {}", COMPOSE_FILE) core_builds = find_templates_build(Application.data.base_services) all_builds = find_templates_build(Application.data.compose_config) services_with_custom_builds: List[str] = [] for image, build in all_builds.items(): if image not in core_builds: # this is used to validate the target Dockerfile: if p := build.get("path"): get_dockerfile_base_image(p, core_builds) services_with_custom_builds.extend(build["services"]) images.add(image) targets: List[str] = [] for service in Application.data.active_services: if Application.data.services and service not in Application.data.services: continue if service in services_with_custom_builds: targets.append(service) if not targets: log.info("No custom images to build") return False clean_targets = get_non_redundant_services(all_builds, targets) docker.client.buildx.bake( targets=list(clean_targets), files=[COMPOSE_FILE], load=True, pull=True, cache=not force, ) if Configuration.swarm_mode: registry = docker.registry.get_host() local_images: List[str] = [] for img in images: new_tag = f"{registry}/{img}" docker.client.tag(img, new_tag) local_images.append(new_tag) # push to the local registry docker.client.image.push(local_images, quiet=False) # remove local tags docker.client.image.remove(local_images, prune=True) # type: ignore log.info("Custom images built and pushed into the local registry") else: log.info("Custom images built") return True