diff --git a/sources/org.osbuild.skopeo b/sources/org.osbuild.skopeo index f236ea62bf..5d56a32c47 100755 --- a/sources/org.osbuild.skopeo +++ b/sources/org.osbuild.skopeo @@ -93,9 +93,9 @@ class SkopeoSource(sources.SourceService): os.chmod(archive_dir, 0o755) source = f"docker://{imagename}@{digest}" - archive_name = containers.archive_name(archive_format) - destination = f"{archive_format}:{archive_dir}/{archive_name}" + dest_path = os.path.join(archive_dir, archive_name) + destination = f"{archive_format}:{dest_path}" extra_args = [] @@ -119,6 +119,10 @@ class SkopeoSource(sources.SourceService): raise RuntimeError( f"Downloaded image {imagename}@{digest} has a id of {downloaded_id}, but expected {image_id}") + if archive_format == "dir": + # fetch the manifest and merge it into the archive + self.merge_manifest(source, dest_path) + # Atomically move download archive into place on successful download with ctx.suppress_oserror(errno.ENOTEMPTY, errno.EEXIST): os.makedirs(f"{self.cache}/{image_id}", exist_ok=True) @@ -128,6 +132,23 @@ class SkopeoSource(sources.SourceService): archive_name = containers.archive_name(desc["image"].get("format", "docker-archive")) return os.path.exists(f"{self.cache}/{checksum}/{archive_name}") + def merge_manifest(self, source, destination): + with tempfile.TemporaryDirectory(prefix="tmp-download-", dir=self.cache) as indexdir: + # download the manifest(s) to a temporary directory + subprocess.run(["skopeo", "copy", "--multi-arch=index-only", source, f"dir:{indexdir}"], + encoding="utf-8", check=True) + + # calculate the checksum of the manifest of the container image in the destination + manifest_path = os.path.join(destination, "manifest.json") + with open(manifest_path, encoding="utf-8") as manifest: + manifest_checksum = hashlib.sha256(manifest.read().encode()).hexdigest() + + # rename the manifest to its checksum + os.rename(manifest_path, os.path.join(destination, manifest_checksum + ".manifest.json")) + + # move the index manifest into the destination + os.rename(os.path.join(indexdir, "manifest.json"), manifest_path) + def main(): service = SkopeoSource.from_args(sys.argv[1:])