Add tagging config to pass params easier (#2234)

* Add tagging config to pass params easier

* Shorter function signatures
This commit is contained in:
Ayaz Salikhov
2025-02-22 01:05:54 +00:00
committed by GitHub
parent 18e09a7872
commit 8fc97cb36b
15 changed files with 148 additions and 205 deletions

View File

@@ -2,12 +2,11 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import logging
from pathlib import Path
import plumbum
from tagging.apps.common_cli_arguments import common_arguments_parser
from tagging.utils.get_platform import unify_aarch64
from tagging.utils.config import Config
from tagging.utils.get_prefix import get_file_prefix_for_platform
docker = plumbum.local["docker"]
@@ -15,44 +14,30 @@ docker = plumbum.local["docker"]
LOGGER = logging.getLogger(__name__)
def apply_tags(
*,
registry: str,
owner: str,
short_image_name: str,
variant: str,
platform: str,
tags_dir: Path,
) -> None:
def apply_tags(config: Config) -> None:
"""
Tags <registry>/<owner>/<short_image_name>:latest with the tags reported by all taggers for this image
Tags <config.full_image()> with the tags reported by all taggers for this image
"""
LOGGER.info(f"Tagging image: {short_image_name}")
LOGGER.info(f"Tagging image: {config.image}")
file_prefix = get_file_prefix_for_platform(platform, variant)
image = f"{registry}/{owner}/{short_image_name}:latest"
filename = f"{file_prefix}-{short_image_name}.txt"
tags = (tags_dir / filename).read_text().splitlines()
file_prefix = get_file_prefix_for_platform(config.platform, config.variant)
filename = f"{file_prefix}-{config.image}.txt"
tags = (config.tags_dir / filename).read_text().splitlines()
for tag in tags:
LOGGER.info(f"Applying tag: {tag}")
docker["tag", image, tag] & plumbum.FG
docker["tag", config.full_image(), tag] & plumbum.FG
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
arg_parser = common_arguments_parser(
registry=True, owner=True, short_image_name=True, variant=True, tags_dir=True
config = common_arguments_parser(
registry=True,
owner=True,
image=True,
variant=True,
platform=True,
tags_dir=True,
)
arg_parser.add_argument(
"--platform",
required=True,
type=str,
choices=["x86_64", "aarch64", "arm64"],
help="Image platform",
)
args = arg_parser.parse_args()
args.platform = unify_aarch64(args.platform)
apply_tags(**vars(args))
apply_tags(config)

View File

@@ -3,17 +3,22 @@
import argparse
from pathlib import Path
from tagging.utils.config import Config
from tagging.utils.get_platform import unify_aarch64
def common_arguments_parser(
*,
registry: bool = False,
owner: bool = False,
short_image_name: bool = False,
image: bool = False,
variant: bool = False,
platform: bool = False,
tags_dir: bool = False,
hist_lines_dir: bool = False,
manifests_dir: bool = False,
) -> argparse.ArgumentParser:
repository: bool = False,
) -> Config:
"""Add common CLI arguments to parser"""
parser = argparse.ArgumentParser()
@@ -30,9 +35,9 @@ def common_arguments_parser(
required=True,
help="Owner of the image",
)
if short_image_name:
if image:
parser.add_argument(
"--short-image-name",
"--image",
required=True,
help="Short image name",
)
@@ -42,6 +47,14 @@ def common_arguments_parser(
required=True,
help="Variant tag prefix",
)
if platform:
parser.add_argument(
"--platform",
required=True,
type=str,
choices=["x86_64", "aarch64", "arm64"],
help="Image platform",
)
if tags_dir:
parser.add_argument(
"--tags-dir",
@@ -63,5 +76,14 @@ def common_arguments_parser(
type=Path,
help="Directory for manifests file",
)
if repository:
parser.add_argument(
"--repository",
required=True,
help="Repository name on GitHub",
)
args = parser.parse_args()
if platform:
args.platform = unify_aarch64(args.platform)
return parser
return Config(**vars(args))

View File

@@ -2,11 +2,11 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import logging
from pathlib import Path
import plumbum
from tagging.apps.common_cli_arguments import common_arguments_parser
from tagging.utils.config import Config
from tagging.utils.get_platform import ALL_PLATFORMS
from tagging.utils.get_prefix import get_file_prefix_for_platform
@@ -15,23 +15,18 @@ docker = plumbum.local["docker"]
LOGGER = logging.getLogger(__name__)
def merge_tags(
*,
short_image_name: str,
variant: str,
tags_dir: Path,
) -> None:
def merge_tags(config: Config) -> None:
"""
Merge tags for x86_64 and aarch64 images when possible.
"""
LOGGER.info(f"Merging tags for image: {short_image_name}")
LOGGER.info(f"Merging tags for image: {config.image}")
all_tags: set[str] = set()
for platform in ALL_PLATFORMS:
file_prefix = get_file_prefix_for_platform(platform, variant)
filename = f"{file_prefix}-{short_image_name}.txt"
file_path = tags_dir / filename
file_prefix = get_file_prefix_for_platform(platform, config.variant)
filename = f"{file_prefix}-{config.image}.txt"
file_path = config.tags_dir / filename
if file_path.exists():
tags = file_path.read_text().splitlines()
all_tags.update(tag.replace(platform + "-", "") for tag in tags)
@@ -62,9 +57,5 @@ def merge_tags(
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
arg_parser = common_arguments_parser(
short_image_name=True, variant=True, tags_dir=True
)
args = arg_parser.parse_args()
merge_tags(**vars(args))
config = common_arguments_parser(image=True, variant=True, tags_dir=True)
merge_tags(config)

View File

@@ -3,7 +3,6 @@
# Distributed under the terms of the Modified BSD License.
import datetime
import logging
from pathlib import Path
from docker.models.containers import Container
@@ -13,6 +12,7 @@ from tagging.hierarchy.get_taggers_and_manifests import (
)
from tagging.manifests.header import ManifestHeader
from tagging.manifests.manifest_interface import ManifestInterface
from tagging.utils.config import Config
from tagging.utils.docker_runner import DockerRunner
from tagging.utils.get_prefix import get_file_prefix, get_tag_prefix
from tagging.utils.git_helper import GitHelper
@@ -25,130 +25,77 @@ MARKDOWN_LINE_BREAK = "<br />"
def write_build_history_line(
*,
registry: str,
owner: str,
short_image_name: str,
hist_lines_dir: Path,
filename: str,
all_tags: list[str],
repository: str,
config: Config, filename: str, all_tags: list[str]
) -> None:
LOGGER.info("Appending build history line")
date_column = f"`{BUILD_TIMESTAMP}`"
image_column = MARKDOWN_LINE_BREAK.join(
f"`{registry}/{owner}/{short_image_name}:{tag_value}`" for tag_value in all_tags
f"`{config.full_image()}:{tag_value}`" for tag_value in all_tags
)
commit_hash = GitHelper.commit_hash()
links_column = MARKDOWN_LINE_BREAK.join(
[
f"[Git diff](https://github.com/{repository}/commit/{commit_hash})",
f"[Dockerfile](https://github.com/{repository}/blob/{commit_hash}/images/{short_image_name}/Dockerfile)",
f"[Git diff](https://github.com/{config.repository}/commit/{commit_hash})",
f"[Dockerfile](https://github.com/{config.repository}/blob/{commit_hash}/images/{config.image}/Dockerfile)",
f"[Build manifest](./{filename})",
]
)
build_history_line = f"| {date_column} | {image_column} | {links_column} |"
hist_lines_dir.mkdir(parents=True, exist_ok=True)
file = hist_lines_dir / f"{filename}.txt"
config.hist_lines_dir.mkdir(parents=True, exist_ok=True)
file = config.hist_lines_dir / f"{filename}.txt"
file.write_text(build_history_line)
LOGGER.info(f"Build history line written to: {file}")
def write_manifest_file(
*,
registry: str,
owner: str,
short_image_name: str,
manifests_dir: Path,
config: Config,
filename: str,
manifests: list[ManifestInterface],
container: Container,
repository: str,
) -> None:
manifest_names = [manifest.__class__.__name__ for manifest in manifests]
LOGGER.info(f"Using manifests: {manifest_names}")
markdown_pieces = [
ManifestHeader.create_header(
registry=registry,
owner=owner,
short_image_name=short_image_name,
build_timestamp=BUILD_TIMESTAMP,
repository=repository,
)
] + [manifest.markdown_piece(container) for manifest in manifests]
markdown_pieces = [ManifestHeader.create_header(config, BUILD_TIMESTAMP)] + [
manifest.markdown_piece(container) for manifest in manifests
]
markdown_content = "\n\n".join(markdown_pieces) + "\n"
manifests_dir.mkdir(parents=True, exist_ok=True)
file = manifests_dir / f"{filename}.md"
config.manifests_dir.mkdir(parents=True, exist_ok=True)
file = config.manifests_dir / f"{filename}.md"
file.write_text(markdown_content)
LOGGER.info(f"Manifest file written to: {file}")
def write_manifest(
*,
registry: str,
owner: str,
short_image_name: str,
variant: str,
hist_lines_dir: Path,
manifests_dir: Path,
repository: str,
) -> None:
LOGGER.info(f"Creating manifests for image: {registry}/{owner}/{short_image_name}")
taggers, manifests = get_taggers_and_manifests(short_image_name)
def write_manifest(config: Config) -> None:
LOGGER.info(f"Creating manifests for image: {config.image}")
taggers, manifests = get_taggers_and_manifests(config.image)
image = f"{registry}/{owner}/{short_image_name}:latest"
file_prefix = get_file_prefix(variant)
file_prefix = get_file_prefix(config.variant)
commit_hash_tag = GitHelper.commit_hash_tag()
filename = f"{file_prefix}-{short_image_name}-{commit_hash_tag}"
filename = f"{file_prefix}-{config.image}-{commit_hash_tag}"
with DockerRunner(image) as container:
tags_prefix = get_tag_prefix(variant)
with DockerRunner(config.full_image()) as container:
tags_prefix = get_tag_prefix(config.variant)
all_tags = [
tags_prefix + "-" + tagger.tag_value(container) for tagger in taggers
]
write_build_history_line(
registry=registry,
owner=owner,
short_image_name=short_image_name,
hist_lines_dir=hist_lines_dir,
filename=filename,
all_tags=all_tags,
repository=repository,
)
write_manifest_file(
registry=registry,
owner=owner,
short_image_name=short_image_name,
manifests_dir=manifests_dir,
filename=filename,
manifests=manifests,
container=container,
repository=repository,
)
write_build_history_line(config, filename, all_tags)
write_manifest_file(config, filename, manifests, container)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
LOGGER.info(f"Current build timestamp: {BUILD_TIMESTAMP}")
arg_parser = common_arguments_parser(
config = common_arguments_parser(
registry=True,
owner=True,
short_image_name=True,
image=True,
variant=True,
hist_lines_dir=True,
manifests_dir=True,
repository=True,
)
arg_parser.add_argument(
"--repository",
required=True,
help="Repository name on GitHub",
)
args = arg_parser.parse_args()
LOGGER.info(f"Current build timestamp: {BUILD_TIMESTAMP}")
write_manifest(**vars(args))
write_manifest(config)

View File

@@ -2,50 +2,40 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import logging
from pathlib import Path
from tagging.apps.common_cli_arguments import common_arguments_parser
from tagging.hierarchy.get_taggers_and_manifests import (
get_taggers_and_manifests,
)
from tagging.utils.config import Config
from tagging.utils.docker_runner import DockerRunner
from tagging.utils.get_prefix import get_file_prefix, get_tag_prefix
LOGGER = logging.getLogger(__name__)
def write_tags_file(
*,
registry: str,
owner: str,
short_image_name: str,
variant: str,
tags_dir: Path,
) -> None:
def write_tags_file(config: Config) -> None:
"""
Writes tags file for the image <registry>/<owner>/<short_image_name>:latest
Writes tags file for the image {config.full_image()}
"""
LOGGER.info(f"Tagging image: {registry}/{owner}/{short_image_name}")
taggers, _ = get_taggers_and_manifests(short_image_name)
LOGGER.info(f"Tagging image: {config.image}")
taggers, _ = get_taggers_and_manifests(config.image)
image = f"{registry}/{owner}/{short_image_name}:latest"
file_prefix = get_file_prefix(variant)
filename = f"{file_prefix}-{short_image_name}.txt"
file_prefix = get_file_prefix(config.variant)
filename = f"{file_prefix}-{config.image}.txt"
tags_prefix = get_tag_prefix(variant)
tags = [f"{registry}/{owner}/{short_image_name}:{tags_prefix}-latest"]
with DockerRunner(image) as container:
tags_prefix = get_tag_prefix(config.variant)
tags = [f"{config.full_image()}:{tags_prefix}-latest"]
with DockerRunner(config.full_image()) as container:
for tagger in taggers:
tagger_name = tagger.__class__.__name__
tag_value = tagger.tag_value(container)
LOGGER.info(
f"Calculated tag, tagger_name: {tagger_name} tag_value: {tag_value}"
)
tags.append(
f"{registry}/{owner}/{short_image_name}:{tags_prefix}-{tag_value}"
)
tags_dir.mkdir(parents=True, exist_ok=True)
file = tags_dir / filename
tags.append(f"{config.full_image()}:{tags_prefix}-{tag_value}")
config.tags_dir.mkdir(parents=True, exist_ok=True)
file = config.tags_dir / filename
file.write_text("\n".join(tags))
LOGGER.info(f"Tags file written to: {file}")
@@ -53,9 +43,7 @@ def write_tags_file(
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
arg_parser = common_arguments_parser(
registry=True, owner=True, short_image_name=True, variant=True, tags_dir=True
config = common_arguments_parser(
registry=True, owner=True, image=True, variant=True, tags_dir=True
)
args = arg_parser.parse_args()
write_tags_file(**vars(args))
write_tags_file(config)

View File

@@ -6,12 +6,12 @@ from tagging.taggers.tagger_interface import TaggerInterface
def get_taggers_and_manifests(
short_image_name: str | None,
image: str | None,
) -> tuple[list[TaggerInterface], list[ManifestInterface]]:
if short_image_name is None:
if image is None:
return [], []
image_description = ALL_IMAGES[short_image_name]
image_description = ALL_IMAGES[image]
parent_taggers, parent_manifests = get_taggers_and_manifests(
image_description.parent_image
)

View File

@@ -2,6 +2,7 @@
# Distributed under the terms of the Modified BSD License.
import plumbum
from tagging.utils.config import Config
from tagging.utils.git_helper import GitHelper
docker = plumbum.local["docker"]
@@ -11,36 +12,30 @@ class ManifestHeader:
"""ManifestHeader doesn't fall under common interface, and we run it separately"""
@staticmethod
def create_header(
registry: str,
owner: str,
short_image_name: str,
build_timestamp: str,
repository: str,
) -> str:
def create_header(config: Config, build_timestamp: str) -> str:
commit_hash = GitHelper.commit_hash()
commit_hash_tag = GitHelper.commit_hash_tag()
commit_message = GitHelper.commit_message()
# Unfortunately, `docker images` doesn't work when specifying `docker.io` as registry
fixed_registry = registry + "/" if registry != "docker.io" else ""
fixed_registry = config.registry + "/" if config.registry != "docker.io" else ""
image_size = docker[
"images",
f"{fixed_registry}{owner}/{short_image_name}:latest",
f"{fixed_registry}{config.owner}/{config.image}:latest",
"--format",
"{{.Size}}",
]().rstrip()
return f"""\
# Build manifest for image: {short_image_name}:{commit_hash_tag}
# Build manifest for image: {config.image}:{commit_hash_tag}
## Build Info
- Build timestamp: {build_timestamp}
- Docker image: `{registry}/{owner}/{short_image_name}:{commit_hash_tag}`
- Docker image: `{config.full_image()}:{commit_hash_tag}`
- Docker image size: {image_size}
- Git commit SHA: [{commit_hash}](https://github.com/{repository}/commit/{commit_hash})
- Git commit SHA: [{commit_hash}](https://github.com/{config.repository}/commit/{commit_hash})
- Git commit message:
```text

22
tagging/utils/config.py Normal file
View File

@@ -0,0 +1,22 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from dataclasses import dataclass
from pathlib import Path
@dataclass(frozen=True)
class Config:
registry: str = ""
owner: str = ""
image: str = ""
variant: str = ""
platform: str = ""
tags_dir: Path = Path()
hist_lines_dir: Path = Path()
manifests_dir: Path = Path()
repository: str = ""
def full_image(self) -> str:
return f"{self.registry}/{self.owner}/{self.image}"