Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 43 additions & 41 deletions Sources/containertool/Extensions/RegistryClient+publish.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,56 +135,58 @@ func publishContainerImage<Source: ImageSource, Destination: ImageDestination>(
)
}

// MARK: Upload application manifest

let manifestDescriptor = try await destination.putManifest(
repository: destinationImage.repository,
reference: destinationImage.reference,
manifest: manifest
)

if verbose {
log("manifest: \(manifestDescriptor.digest) (\(manifestDescriptor.size) bytes)")
// Determine the tags to push. Always include the reference from --repository (defaults to 'latest').
// If --tag is provided and differs from the repository reference, push to both.
var tagsToPublish: [any ImageReference.Reference] = [destinationImage.reference]
if let tag {
let tagReference = try ImageReference.Tag(tag)
// Avoid duplicates if --tag matches the reference already in --repository
if "\(tagReference)" != "\(destinationImage.reference)" {
tagsToPublish.insert(tagReference, at: 0)
}
}

// MARK: Create application index
// MARK: Upload application manifest and index for each tag

let index = ImageIndex(
schemaVersion: 2,
mediaType: "application/vnd.oci.image.index.v1+json",
manifests: [
ContentDescriptor(
mediaType: manifestDescriptor.mediaType,
digest: manifestDescriptor.digest,
size: Int64(manifestDescriptor.size),
platform: .init(architecture: architecture, os: os)
)
]
)
for tagReference in tagsToPublish {
let manifestDescriptor = try await destination.putManifest(
repository: destinationImage.repository,
reference: tagReference,
manifest: manifest
)

// MARK: Upload application manifest
if verbose {
log("manifest (\(tagReference)): \(manifestDescriptor.digest) (\(manifestDescriptor.size) bytes)")
}

let indexDescriptor = try await destination.putIndex(
repository: destinationImage.repository,
reference: destinationImage.reference,
index: index
)
let index = ImageIndex(
schemaVersion: 2,
mediaType: "application/vnd.oci.image.index.v1+json",
manifests: [
ContentDescriptor(
mediaType: manifestDescriptor.mediaType,
digest: manifestDescriptor.digest,
size: Int64(manifestDescriptor.size),
platform: .init(architecture: architecture, os: os)
)
]
)

if verbose {
log("index: \(indexDescriptor.digest) (\(indexDescriptor.size) bytes)")
}
let indexDescriptor = try await destination.putIndex(
repository: destinationImage.repository,
reference: tagReference,
index: index
)

// Use the index digest if the user did not provide a human-readable tag
// To support multiarch images, we should also create an an index pointing to
// this manifest.
let reference: ImageReference.Reference
if let tag {
reference = try ImageReference.Tag(tag)
} else {
reference = try ImageReference.Digest(indexDescriptor.digest)
if verbose {
log("index (\(tagReference)): \(indexDescriptor.digest) (\(indexDescriptor.size) bytes)")
}
}

// Return the primary tag (--tag if provided, otherwise the repository reference)
var result = destinationImage
result.reference = reference
if let tag {
result.reference = try ImageReference.Tag(tag)
}
return result
}