mirror of
https://github.com/d9pouces/id_configurator.git
synced 2026-11-09 01:47:21 +01:00
feat: enhance Docker image tagging and add publication functionality
Signed-off-by: d9pouces <github@19pouces.net>
This commit is contained in:
+4
-4
@@ -93,11 +93,12 @@ def build_docker_image(
|
|||||||
"""Build the Docker image."""
|
"""Build the Docker image."""
|
||||||
if push and not registry:
|
if push and not registry:
|
||||||
raise ValueError("--push requires --registry to be set.")
|
raise ValueError("--push requires --registry to be set.")
|
||||||
id_configurator_version = config.get_project_version()
|
version = config.get_project_version()
|
||||||
|
name = config.get_project_name()
|
||||||
values = config.get_parameters()
|
values = config.get_parameters()
|
||||||
package_build = DOCKER_FILE_BUILDER.format(**values)
|
package_build = DOCKER_FILE_BUILDER.format(**values)
|
||||||
v_tag = f"id_configurator:{id_configurator_version}"
|
v_tag = f"{name}:{version}"
|
||||||
l_tag = "id_configurator:latest"
|
l_tag = f"{name}:latest"
|
||||||
image_install = DOCKER_FILE_INSTALL.format(**values)
|
image_install = DOCKER_FILE_INSTALL.format(**values)
|
||||||
image_build = DOCKER_FILE_RUNTIME.format(**values)
|
image_build = DOCKER_FILE_RUNTIME.format(**values)
|
||||||
with (
|
with (
|
||||||
@@ -136,7 +137,6 @@ def build_docker_image(
|
|||||||
typer.echo(" ".join(cmd))
|
typer.echo(" ".join(cmd))
|
||||||
subprocess.check_call(cmd, cwd=config.source_dir)
|
subprocess.check_call(cmd, cwd=config.source_dir)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
print(package_build + image_install + image_build)
|
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ class SourceConfig:
|
|||||||
python_versions.append(matcher.group(1))
|
python_versions.append(matcher.group(1))
|
||||||
return python_versions
|
return python_versions
|
||||||
|
|
||||||
|
def get_project_name(self) -> str:
|
||||||
|
"""Get the name of InterDiode from the pyproject.toml file."""
|
||||||
|
return self.raw_metadata["project"]["name"]
|
||||||
|
|
||||||
def get_project_version(self) -> str:
|
def get_project_version(self) -> str:
|
||||||
"""Get the version of InterDiode from the pyproject.toml file."""
|
"""Get the version of InterDiode from the pyproject.toml file."""
|
||||||
return self.raw_metadata["project"]["version"]
|
return self.raw_metadata["project"]["version"]
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import typer
|
||||||
|
|
||||||
|
from id_tools.config import SourceConfig
|
||||||
|
|
||||||
|
app = typer.Typer(
|
||||||
|
help="Publish the documentation and release announcements to external destinations."
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command(name="sign-docker", help="Signe l'image Docker sur le registre")
|
||||||
|
def sign_docker_image(
|
||||||
|
registry: str = typer.Option(
|
||||||
|
"interdiode", help="Registre cible pour l'image Docker"
|
||||||
|
),
|
||||||
|
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"
|
||||||
|
),
|
||||||
|
cosign_password: str = typer.Option(
|
||||||
|
"", envvar="COSIGN_PASSWORD", help="Mot de passe de la clé privée Cosign"
|
||||||
|
),
|
||||||
|
cosign_private_key: str = typer.Option(
|
||||||
|
"", envvar="COSIGN_PRIVATE_KEY", help="Contenu de la clé privée Cosign"
|
||||||
|
),
|
||||||
|
):
|
||||||
|
config = SourceConfig()
|
||||||
|
version = config.get_project_version()
|
||||||
|
image = config.get_project_name()
|
||||||
|
v_tag = f"{image}:{version}"
|
||||||
|
l_tag = f"{image}:latest"
|
||||||
|
destinations = [f"{registry}/{v_tag}", f"{registry}/{l_tag}"]
|
||||||
|
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
|
||||||
|
)
|
||||||
|
digest_references = {
|
||||||
|
get_reference_from_tag(destination) for destination in destinations
|
||||||
|
}
|
||||||
|
if cosign_password and cosign_private_key:
|
||||||
|
with (
|
||||||
|
tempfile.NamedTemporaryFile(mode="w", suffix=".key", delete=True) as key_fd,
|
||||||
|
tempfile.TemporaryDirectory() as tmp_dir,
|
||||||
|
):
|
||||||
|
key_fd.write(cosign_private_key)
|
||||||
|
key_fd.flush()
|
||||||
|
cosign_env = {**os.environ, "COSIGN_PASSWORD": cosign_password}
|
||||||
|
trivy_report = os.path.join(tmp_dir, "trivy_report.json")
|
||||||
|
for digest_reference in digest_references:
|
||||||
|
# noinspection SpellCheckingInspection
|
||||||
|
cmd = [
|
||||||
|
"trivy",
|
||||||
|
"image",
|
||||||
|
"--format",
|
||||||
|
"json",
|
||||||
|
"--output",
|
||||||
|
trivy_report,
|
||||||
|
digest_reference,
|
||||||
|
]
|
||||||
|
subprocess.run(cmd, text=True, check=True, cwd=config.source_dir)
|
||||||
|
cmd = [
|
||||||
|
"cosign",
|
||||||
|
"attest",
|
||||||
|
"--key",
|
||||||
|
key_fd.name,
|
||||||
|
"--type",
|
||||||
|
"vuln",
|
||||||
|
"--predicate",
|
||||||
|
trivy_report,
|
||||||
|
digest_reference,
|
||||||
|
]
|
||||||
|
subprocess.run(cmd, text=True, check=True, cwd=config.source_dir)
|
||||||
|
cmd = [
|
||||||
|
"cosign",
|
||||||
|
"sign",
|
||||||
|
"--yes",
|
||||||
|
"--recursive",
|
||||||
|
"--key",
|
||||||
|
key_fd.name,
|
||||||
|
digest_reference,
|
||||||
|
]
|
||||||
|
subprocess.check_call(cmd, env=cosign_env)
|
||||||
|
cmd = [
|
||||||
|
"cosign",
|
||||||
|
"verify-attestation",
|
||||||
|
"--key",
|
||||||
|
config.cosign_key_url,
|
||||||
|
"--type",
|
||||||
|
"vuln",
|
||||||
|
digest_reference,
|
||||||
|
]
|
||||||
|
subprocess.check_call(cmd, env=cosign_env)
|
||||||
|
cmd = [
|
||||||
|
"cosign",
|
||||||
|
"verify",
|
||||||
|
"--key",
|
||||||
|
config.cosign_key_url,
|
||||||
|
digest_reference,
|
||||||
|
]
|
||||||
|
subprocess.check_call(cmd, env=cosign_env)
|
||||||
|
typer.echo(f"✓ Image signée : {registry}/{v_tag} et {registry}/{l_tag}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_reference_from_tag(destination: str) -> str:
|
||||||
|
# noinspection SpellCheckingInspection
|
||||||
|
cmd = [
|
||||||
|
"docker",
|
||||||
|
"buildx",
|
||||||
|
"imagetools",
|
||||||
|
"inspect",
|
||||||
|
destination,
|
||||||
|
"--format",
|
||||||
|
"{{json .Manifest}}",
|
||||||
|
]
|
||||||
|
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
|
manifest = json.loads(result.stdout)
|
||||||
|
image_name = destination.rsplit(":", 1)[0]
|
||||||
|
reference = f"{image_name}@{manifest['digest']}"
|
||||||
|
return reference
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[tox]
|
[tox]
|
||||||
env_list = py313,py314,coverage
|
env_list = py313,py314,coverage,packaging,publish,module-python
|
||||||
isolated_build = true
|
isolated_build = true
|
||||||
requires =
|
requires =
|
||||||
tox>=4
|
tox>=4
|
||||||
@@ -26,6 +26,38 @@ commands =
|
|||||||
python3 -m coverage xml -o .tox/coverage.xml --ignore
|
python3 -m coverage xml -o .tox/coverage.xml --ignore
|
||||||
python3 -m coverage report -m --fail-under=94 --ignore --skip-covered
|
python3 -m coverage report -m --fail-under=94 --ignore --skip-covered
|
||||||
|
|
||||||
|
[testenv:packaging]
|
||||||
|
package = skip
|
||||||
|
change_dir = {tox_root}
|
||||||
|
allowlist_externals =
|
||||||
|
docker
|
||||||
|
dependency_groups = dev
|
||||||
|
description =
|
||||||
|
Generate the docker images for the different platform
|
||||||
|
depends =
|
||||||
|
update
|
||||||
|
commands =
|
||||||
|
python3 -m id_tools build docker --platform linux/amd64,linux/arm64/v8
|
||||||
|
|
||||||
|
[testenv:publish]
|
||||||
|
package = skip
|
||||||
|
change_dir = {tox_root}
|
||||||
|
dependency_groups = dev
|
||||||
|
description =
|
||||||
|
Build and publish the image to Docker Hub.
|
||||||
|
The version is taken from the pyproject.toml file.
|
||||||
|
Make sure that you have the rights to upload the image to Docker Hub.
|
||||||
|
commands =
|
||||||
|
python3 -m uv sync --all-groups --active
|
||||||
|
python3 -m id_tools build docker --platform linux/amd64,linux/arm64/v8 --push
|
||||||
|
python3 -m id_tools publication sign-docker
|
||||||
|
docker =
|
||||||
|
passenv =
|
||||||
|
COSIGN_PASSWORD
|
||||||
|
COSIGN_PRIVATE_KEY
|
||||||
|
DOCKER_HUB_LOGIN
|
||||||
|
DOCKER_HUB_TOKEN
|
||||||
|
|
||||||
[docker:pypi-repo]
|
[docker:pypi-repo]
|
||||||
dockerfile = {tox_root}/tests/dockerfiles/repos/pypi-repo.Dockerfile
|
dockerfile = {tox_root}/tests/dockerfiles/repos/pypi-repo.Dockerfile
|
||||||
expose =
|
expose =
|
||||||
@@ -45,5 +77,5 @@ docker =
|
|||||||
allowlist_externals =
|
allowlist_externals =
|
||||||
docker
|
docker
|
||||||
commands =
|
commands =
|
||||||
python3 -m id_tools test render-config tests/configs/Python.yaml.tmpl {toxworkdir}/configs/Python.yaml
|
python3 -m id_tools test render-config {tox_root}/tests/configs/Python.yaml.tmpl {env_dir}/configs/Python.yaml
|
||||||
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={toxworkdir}/configs -f tests/dockerfiles/Python.Dockerfile -t id-configurator-python .
|
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Python.Dockerfile -t id-configurator-python .
|
||||||
|
|||||||
Reference in New Issue
Block a user