import logging import subprocess import tempfile import typer from id_tools.config import SourceConfig app = typer.Typer(help="Build utilities for JS/CSS files and emails templates") logger = logging.getLogger(__name__) # noinspection SpellCheckingInspection DOCKER_FILE_BUILDER = r"""# This file is part of Interdiode # Copyright (c) {build_year} InterDiode # All Rights Reserved ######################################################################################################################## # This file is generated by id_tools/build.py. Do not edit it manually. ######################################################################################################################## FROM dhi.io/python:{python_version}-{docker_version} AS interdiode-base ######################################################################################################################## # generate the package and download required some tools in a first image ######################################################################################################################## FROM interdiode-base AS interdiode-builder ENV{docker_env} USER root RUN mkdir -p /src /usr/local/interdiode RUN chown -R {docker_user}:{docker_group} /src /usr/local/interdiode COPY ./pyproject.toml /src/pyproject.toml COPY ./README.md /src/README.md COPY ./LICENSE /src/LICENSE COPY ./id_configurator /src/id_configurator COPY ./tests /src/tests RUN chown -R {docker_user}:{docker_group} /src USER {docker_user} RUN python3 -m pip install --user pip uv --upgrade WORKDIR /src RUN python3 -m uv build """ # noinspection SpellCheckingInspection DOCKER_FILE_INSTALL = r""" ######################################################################################################################## # install the package without the build dependencies ######################################################################################################################## FROM interdiode-base AS interdiode-install ENV{docker_env} COPY --from=interdiode-builder /src/dist /tmp/dist RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confnew" --allow-downgrades --allow-remove-essential --allow-unauthenticated -fuy dist-upgrade RUN DEBIAN_FRONTEND=noninteractive apt-get install --yes -qq --no-install-recommends build-essential RUN mkdir -p /src/build /chroot/usr/local/bin /usr/local/interdiode \ && chown -R {docker_user}:{docker_group} /usr/local/interdiode /tmp/dist USER {docker_user} RUN /bin/sh -c "python3 -m pip install --user /tmp/dist/*.whl" """ # noinspection SpellCheckingInspection DOCKER_FILE_RUNTIME = r"""######################################################################################################################## # copy the installed packages ######################################################################################################################## FROM interdiode-base AS interdiode-runtime ENV{docker_env} LABEL org.opencontainers.image.authors="contact@mail.interdiode.fr" LABEL org.opencontainers.image.created="{build_date}" LABEL org.opencontainers.image.description="Air gap made simple!" LABEL org.opencontainers.image.documentation="{documentation_url}" LABEL org.opencontainers.image.licenses="LicenseRef-InterDiode-Commercial-1.0" LABEL org.opencontainers.image.revision="{vcs_revision}" LABEL org.opencontainers.image.title="InterDiode Configurator" LABEL org.opencontainers.image.url="{interdiode_url}" LABEL org.opencontainers.image.vendor="InterDiode" LABEL org.opencontainers.image.version="{id_configurator_version}" LABEL description="Air gap made simple!" COPY --from=interdiode-install --chown=0:0 /chroot/usr/local/bin /usr/local/bin COPY --from=interdiode-install /usr/local/interdiode /usr/local/interdiode RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confnew" --allow-downgrades --allow-remove-essential --allow-unauthenticated -fuy dist-upgrade \ && DEBIAN_FRONTEND=noninteractive apt-get install --yes -qq --no-install-recommends gpg gpg-agent \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /usr/local/interdiode \ && chown -R {docker_user}:{docker_group} /usr/local/interdiode COPY ./LICENSE /chroot/licenses/LICENSE COPY ./LICENSE /chroot/usr/share/doc/interdiode/LICENSE WORKDIR / """ def build_docker_image( config: SourceConfig, registry: str | None = None, platform: str = "linux/amd64", push: bool = False, ) -> bool: """Build the Docker image.""" if push and not registry: raise ValueError("--push requires --registry to be set.") version = config.get_project_version() name = config.get_project_name() values = config.get_parameters() package_build = DOCKER_FILE_BUILDER.format(**values) v_tag = f"{name}:{version}" l_tag = f"{name}:latest" image_install = DOCKER_FILE_INSTALL.format(**values) image_build = DOCKER_FILE_RUNTIME.format(**values) with ( tempfile.NamedTemporaryFile( mode="w", delete=True, prefix="Dockerfile.", suffix=".package" ) as fd, ): fd.write(package_build) fd.write(image_install) fd.write(image_build) fd.flush() # noinspection SpellCheckingInspection cmd = [ "docker", "buildx", "build", "--no-cache", "--attest", "type=provenance,mode=max,version=v1", "--attest", "type=sbom", "--platform", platform, "-f", fd.name, ] if registry: cmd += ["-t", f"{registry}/{v_tag}", "-t", f"{registry}/{l_tag}"] else: cmd += ["-t", f"{registry}/{v_tag}", "-t", f"{registry}/{l_tag}"] if push and registry: cmd.insert(3, "--push") cmd.append(".") try: typer.echo(f"Building Docker image {v_tag} for platform {platform}...") typer.echo(" ".join(cmd)) subprocess.check_call(cmd, cwd=config.source_dir) except subprocess.CalledProcessError: return False return True # noinspection SpellCheckingInspection @app.command(name="docker", help="Build the Docker image.") def docker_image( registry: str = typer.Option( "interdiode", help="The registry to publish the Docker image to." ), platform: str = typer.Option( "linux/amd64", help="Platform(s) of the Docker image." ), docker_version: str = typer.Option( "debian13-dev", help="Version of the base Docker image." ), push: bool = typer.Option( False, "--push", help="Push the built image(s) to the registry." ), docker_login: str = typer.Option( "", envvar="DOCKER_HUB_LOGIN", help="Nom d'utilisateur Docker" ), docker_token: str = typer.Option( "", envvar="DOCKER_HUB_TOKEN", help="Jeton d'accès Docker" ), ): config = SourceConfig(docker_version) if docker_login and docker_token: cmd = ["docker", "login", "-u", docker_login, "--password-stdin"] subprocess.run( cmd, input=docker_token, text=True, check=True, cwd=config.source_dir ) success = build_docker_image( config, registry=registry, platform=platform, push=push ) if not success: raise typer.Exit(code=1)