Skip to content

Commit 03b03ce

Browse files
committed
fix(docker): improve manifest architecture detection
- Handle both manifest list (multi-platform) and single manifest formats - For single manifests, parse formatted output to get platform info - Architecture info is not at root level in single-platform manifests - Falls back to multiple detection methods for robustness The previous validation assumed architecture would be at the root of the manifest JSON, but single-platform images pushed with 'docker buildx build --push' have a different structure where the architecture is embedded in the config blob. This fix checks the manifest mediaType and handles both cases: 1. Manifest list: reads platform from manifests array 2. Single manifest: uses formatted inspect output to get platform
1 parent c797896 commit 03b03ce

File tree

1 file changed

+76
-16
lines changed

1 file changed

+76
-16
lines changed

docker/scripts/docker.py

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ def _validate_image_architecture(self, expected_version: str) -> bool:
493493
"""
494494
full_uri = f"{self.registry}/{self.image_name}:{expected_version}"
495495

496-
# Use docker buildx imagetools to get architecture from image
496+
# First, try to get the raw manifest
497497
result = subprocess.run(
498498
["docker", "buildx", "imagetools", "inspect", "--raw", full_uri],
499499
capture_output=True,
@@ -507,28 +507,88 @@ def _validate_image_architecture(self, expected_version: str) -> bool:
507507

508508
try:
509509
manifest_data = json.loads(result.stdout)
510-
arch = manifest_data.get("architecture", "")
511-
os_name = manifest_data.get("os", "")
512510

513-
if not arch or not os_name:
514-
print(f"❌ Architecture metadata not found in image", file=sys.stderr)
511+
# Check if this is a manifest list (multi-platform) or single manifest
512+
media_type = manifest_data.get("mediaType", "")
513+
schema_version = manifest_data.get("schemaVersion", 0)
514+
515+
# Handle manifest list (multi-platform images)
516+
if media_type in [
517+
"application/vnd.docker.distribution.manifest.list.v2+json",
518+
"application/vnd.oci.image.index.v1+json",
519+
]:
520+
manifests = manifest_data.get("manifests", [])
521+
if not manifests:
522+
print(f"❌ No manifests found in manifest list", file=sys.stderr)
523+
return False
524+
525+
# Look for linux/amd64 platform
526+
for manifest in manifests:
527+
platform = manifest.get("platform", {})
528+
arch = platform.get("architecture", "")
529+
os_name = platform.get("os", "")
530+
531+
if arch == "amd64" and os_name == "linux":
532+
print(f" Architecture: {os_name}/{arch} (from manifest list)", file=sys.stderr)
533+
print(f"✅ Valid production architecture: {os_name}/{arch}", file=sys.stderr)
534+
return True
535+
536+
print(f"❌ linux/amd64 platform not found in manifest list", file=sys.stderr)
537+
return False
538+
539+
# Handle single manifest (single-platform images)
540+
# For single manifests, architecture is in the config blob, not the manifest
541+
# We need to inspect without --raw to get platform info
542+
result_formatted = subprocess.run(
543+
["docker", "buildx", "imagetools", "inspect", full_uri],
544+
capture_output=True,
545+
text=True,
546+
check=False,
547+
)
548+
549+
if result_formatted.returncode != 0:
550+
print(f"❌ Failed to inspect image (formatted): {result_formatted.stderr}", file=sys.stderr)
515551
return False
516552

517-
arch_info = f"{os_name}/{arch}"
518-
print(f" Architecture: {arch_info}", file=sys.stderr)
553+
# Parse the human-readable output for platform info
554+
# Output format: "Name: <image>\nMediaType: ...\nDigest: ...\nPlatform: linux/amd64"
555+
output = result_formatted.stdout
556+
platform_line = None
557+
for line in output.split("\n"):
558+
if line.strip().startswith("Platform:"):
559+
platform_line = line.strip()
560+
break
561+
562+
if not platform_line:
563+
# Fallback: try to get architecture from manifest's config
564+
arch = manifest_data.get("architecture", "")
565+
os_name = manifest_data.get("os", "")
566+
567+
if arch and os_name:
568+
arch_info = f"{os_name}/{arch}"
569+
print(f" Architecture: {arch_info} (from manifest)", file=sys.stderr)
570+
571+
if arch == "amd64" and os_name == "linux":
572+
print(f"✅ Valid production architecture: {arch_info}", file=sys.stderr)
573+
return True
574+
575+
print(f"❌ Invalid architecture: {arch_info}", file=sys.stderr)
576+
print(f" Production images MUST be linux/amd64", file=sys.stderr)
577+
return False
578+
579+
print(f"❌ Architecture metadata not found in image", file=sys.stderr)
580+
print(f" Debug: manifest mediaType={media_type}, schemaVersion={schema_version}", file=sys.stderr)
581+
return False
519582

520-
# Calculate image size
521-
layers = manifest_data.get("layers", [])
522-
if layers:
523-
size_bytes = sum(layer.get("size", 0) for layer in layers)
524-
print(f" Size: {self._format_size(size_bytes)}", file=sys.stderr)
583+
# Parse platform from the formatted output (e.g., "Platform: linux/amd64")
584+
platform_str = platform_line.split(":", 1)[1].strip()
585+
print(f" Architecture: {platform_str}", file=sys.stderr)
525586

526-
# Validate it's linux/amd64
527-
if arch == "amd64" and os_name == "linux":
528-
print(f"✅ Valid production architecture: {arch_info}", file=sys.stderr)
587+
if platform_str == "linux/amd64":
588+
print(f"✅ Valid production architecture: {platform_str}", file=sys.stderr)
529589
return True
530590

531-
print(f"❌ Invalid architecture: {arch_info}", file=sys.stderr)
591+
print(f"❌ Invalid architecture: {platform_str}", file=sys.stderr)
532592
print(f" Production images MUST be linux/amd64", file=sys.stderr)
533593
return False
534594

0 commit comments

Comments
 (0)