112 lines
3.5 KiB
Python
112 lines
3.5 KiB
Python
from subprocess import PIPE, run
|
|
from typing import Literal, NotRequired, TypedDict, get_args, List
|
|
from json import loads
|
|
|
|
Platforms = Literal["linux/amd64", "linux/amd64/v2", "linux/amd64/v3", "linux/arm64", "linux/riscv64", "linux/ppc64le", "linux/s390x", "linux/386", "linux/mips64le", "linux/mips64", "linux/arm/v7", "linux/arm/v6"]
|
|
supported_platforms: list[Platforms] = list(get_args(Platforms))
|
|
|
|
Progress = Literal["auto", "plain", "tty"]
|
|
|
|
class Platform(TypedDict):
|
|
architecture: str
|
|
os: str
|
|
variant: NotRequired[str]
|
|
|
|
|
|
class ManifestsItem(TypedDict):
|
|
mediaType: str
|
|
digest: str
|
|
size: int
|
|
platform: Platform
|
|
|
|
|
|
class Manifest(TypedDict):
|
|
schemaVersion: int
|
|
mediaType: str
|
|
digest: str
|
|
size: int
|
|
manifests: List[ManifestsItem]
|
|
|
|
|
|
class Config(TypedDict):
|
|
Env: List[str]
|
|
Cmd: List[str]
|
|
|
|
|
|
class Rootfs(TypedDict):
|
|
type: str
|
|
diff_ids: List[str]
|
|
|
|
|
|
class HistoryItem(TypedDict):
|
|
created: str
|
|
created_by: str
|
|
empty_layer: NotRequired[bool]
|
|
|
|
|
|
class PlatformImage(TypedDict):
|
|
created: str
|
|
architecture: str
|
|
variant: NotRequired[str]
|
|
os: str
|
|
config: Config
|
|
rootfs: Rootfs
|
|
history: List[HistoryItem]
|
|
|
|
|
|
class ImagetoolsInspectResult(TypedDict):
|
|
name: str
|
|
manifest: Manifest
|
|
image: dict[Platform, PlatformImage]
|
|
|
|
def buildx_imagetools_inspect(repository: str, tag: str) -> ImagetoolsInspectResult:
|
|
label = f"{repository}:{tag}"
|
|
process = run(["docker", "buildx", "imagetools", "inspect", label, "--format", "{{json .}}"], check=True, stdout=PIPE)
|
|
output = process.stdout.decode("utf-8")
|
|
return loads(output)
|
|
|
|
def semver_tags(version: str):
|
|
parts = version.split('.')
|
|
return list('.'.join(parts[:i]) for i in range(1, len(parts) + 1))
|
|
|
|
def pull(repository: str, tag: str):
|
|
label = f"{repository}:{tag}"
|
|
run(["docker", "pull", label], check=True)
|
|
return label
|
|
|
|
def push_image(repository: str, tag: str):
|
|
label = f"{repository}:{tag}"
|
|
run(["docker", "push", label], check=True)
|
|
return label
|
|
|
|
def create_manifest(repository: str, manifest_tag: str, image_tags: list[str], push: bool = True, write_command: bool = False):
|
|
raise Exception("Creation of manifests is not yet supported!")
|
|
manifest = f"{repository}:{manifest_tag}"
|
|
images = [f"{repository}:{tag}" for tag in image_tags]
|
|
for image_tag in image_tags:
|
|
pull(repository, image_tag)
|
|
command = ["docker", "manifest", "create", manifest, *images]
|
|
if write_command:
|
|
print(" ".join(command))
|
|
run(command, check=True)
|
|
if push:
|
|
return push_image(repository, manifest_tag)
|
|
return manifest
|
|
|
|
def buildx(repository: str, tags: list[str], build_platforms: list[Platforms], dockerfile: str = "Dockerfile", build_args: dict[str, str] | None = None, directory: str = ".", push: bool = True, pull: bool = False, progress: Progress = "auto", write_command: bool = False):
|
|
if build_args is None:
|
|
build_args = dict()
|
|
labels = [f"{repository}:{tag}" for tag in tags]
|
|
command = list(filter(None, ["docker", "buildx", "build",
|
|
"--platform", ",".join(build_platforms),
|
|
*[t for (key, value) in build_args.items() for t in ("--build-arg", f"{key}={value}")],
|
|
"--file", dockerfile,
|
|
*[t for label in labels for t in ("--tag", label)],
|
|
f"--progress={progress}",
|
|
"--pull" if pull else None,
|
|
"--push" if push else None,
|
|
directory]))
|
|
if write_command:
|
|
print(" ".join(command))
|
|
run(command, check=True)
|