From 94b88611bbac0972670a12beef53041dedc59e9c Mon Sep 17 00:00:00 2001 From: Euan Harris Date: Tue, 8 Jul 2025 10:43:13 +0100 Subject: [PATCH 1/2] ContainerRegistry: Remove return-type overloaded version of executeRequestThrowing() --- Sources/ContainerRegistry/Blobs.swift | 4 +-- Sources/ContainerRegistry/Manifests.swift | 26 +++++++++---------- .../ContainerRegistry/RegistryClient.swift | 24 ----------------- Sources/ContainerRegistry/Tags.swift | 6 ++++- 4 files changed, 20 insertions(+), 40 deletions(-) diff --git a/Sources/ContainerRegistry/Blobs.swift b/Sources/ContainerRegistry/Blobs.swift index ce2395f..2e409d4 100644 --- a/Sources/ContainerRegistry/Blobs.swift +++ b/Sources/ContainerRegistry/Blobs.swift @@ -91,11 +91,11 @@ public extension RegistryClient { func getBlob(repository: ImageReference.Repository, digest: ImageReference.Digest) async throws -> Response { - try await executeRequestThrowing( + let (data, _) = try await executeRequestThrowing( .get(repository, path: "blobs/\(digest)", accepting: ["application/octet-stream"]), decodingErrors: [.notFound] ) - .data + return try decoder.decode(Response.self, from: data) } /// Uploads a blob to the registry. diff --git a/Sources/ContainerRegistry/Manifests.swift b/Sources/ContainerRegistry/Manifests.swift index ac8ccaf..b1681d8 100644 --- a/Sources/ContainerRegistry/Manifests.swift +++ b/Sources/ContainerRegistry/Manifests.swift @@ -17,9 +17,7 @@ public extension RegistryClient { repository: ImageReference.Repository, reference: any ImageReference.Reference, manifest: ImageManifest - ) async throws - -> String - { + ) async throws -> String { // See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests let httpResponse = try await executeRequestThrowing( // All blob uploads have Content-Type: application/octet-stream on the wire, even if mediatype is different @@ -44,11 +42,12 @@ public extension RegistryClient { .absoluteString } - func getManifest(repository: ImageReference.Repository, reference: any ImageReference.Reference) async throws - -> ImageManifest - { + func getManifest( + repository: ImageReference.Repository, + reference: any ImageReference.Reference + ) async throws -> ImageManifest { // See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests - try await executeRequestThrowing( + let (data, _) = try await executeRequestThrowing( .get( repository, path: "manifests/\(reference)", @@ -59,14 +58,15 @@ public extension RegistryClient { ), decodingErrors: [.notFound] ) - .data + return try decoder.decode(ImageManifest.self, from: data) } - func getIndex(repository: ImageReference.Repository, reference: any ImageReference.Reference) async throws - -> ImageIndex - { + func getIndex( + repository: ImageReference.Repository, + reference: any ImageReference.Reference + ) async throws -> ImageIndex { // See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests - try await executeRequestThrowing( + let (data, _) = try await executeRequestThrowing( .get( repository, path: "manifests/\(reference)", @@ -77,6 +77,6 @@ public extension RegistryClient { ), decodingErrors: [.notFound] ) - .data + return try decoder.decode(ImageIndex.self, from: data) } } diff --git a/Sources/ContainerRegistry/RegistryClient.swift b/Sources/ContainerRegistry/RegistryClient.swift index af6c62a..3902cd8 100644 --- a/Sources/ContainerRegistry/RegistryClient.swift +++ b/Sources/ContainerRegistry/RegistryClient.swift @@ -308,27 +308,6 @@ extension RegistryClient { } } - /// Execute an HTTP request with no request body, decoding the JSON response - /// - Parameters: - /// - request: The HTTP request to execute. - /// - success: The HTTP status code expected if the request is successful. - /// - errors: Expected error codes for which the registry sends structured error messages. - /// - Returns: An asynchronously-delivered tuple that contains the raw response body as a Data instance, and a HTTPURLResponse. - /// - Throws: If the server response is unexpected or indicates that an error occurred. - func executeRequestThrowing( - _ request: RegistryOperation, - expectingStatus success: HTTPResponse.Status = .ok, - decodingErrors errors: [HTTPResponse.Status] - ) async throws -> (data: Response, response: HTTPResponse) { - let (data, httpResponse) = try await executeRequestThrowing( - request, - expectingStatus: success, - decodingErrors: errors - ) - let decoded = try decoder.decode(Response.self, from: data) - return (decoded, httpResponse) - } - /// Execute an HTTP request uploading a request body. /// - Parameters: /// - operation: The Registry operation to execute. @@ -337,9 +316,6 @@ extension RegistryClient { /// - errors: Expected error codes for which the registry sends structured error messages. /// - Returns: An asynchronously-delivered tuple that contains the raw response body as a Data instance, and a HTTPURLResponse. /// - Throws: If the server response is unexpected or indicates that an error occurred. - /// - /// A plain Data version of this function is required because Data is Encodable and encodes to base64. - /// Accidentally encoding data blobs will cause digests to fail and runtimes to be unable to run the images. func executeRequestThrowing( _ operation: RegistryOperation, uploading payload: Data, diff --git a/Sources/ContainerRegistry/Tags.swift b/Sources/ContainerRegistry/Tags.swift index 59f6f87..fe24dc2 100644 --- a/Sources/ContainerRegistry/Tags.swift +++ b/Sources/ContainerRegistry/Tags.swift @@ -15,6 +15,10 @@ public extension RegistryClient { func getTags(repository: ImageReference.Repository) async throws -> Tags { // See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-tags - try await executeRequestThrowing(.get(repository, path: "tags/list"), decodingErrors: [.notFound]).data + let (data, _) = try await executeRequestThrowing( + .get(repository, path: "tags/list"), + decodingErrors: [.notFound] + ) + return try decoder.decode(Tags.self, from: data) } } From d6374b5c07182ca901d2efc515d9340bf488f11e Mon Sep 17 00:00:00 2001 From: Euan Harris Date: Tue, 8 Jul 2025 12:04:26 +0100 Subject: [PATCH 2/2] ContainerRegistry: Remove return-type overloaded version of getBlob() --- Sources/ContainerRegistry/Blobs.swift | 22 ------------------- .../RegistryClient+ImageConfiguration.swift | 17 ++++++++------ 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/Sources/ContainerRegistry/Blobs.swift b/Sources/ContainerRegistry/Blobs.swift index 2e409d4..d3cfc5d 100644 --- a/Sources/ContainerRegistry/Blobs.swift +++ b/Sources/ContainerRegistry/Blobs.swift @@ -76,28 +76,6 @@ public extension RegistryClient { .data } - /// Fetches a blob and tries to decode it as a JSON object. - /// - /// - Parameters: - /// - repository: Name of the repository containing the blob. - /// - digest: Digest of the blob. - /// - Returns: The decoded object. - /// - Throws: If the blob download fails or the blob cannot be decoded. - /// - /// Some JSON objects, such as ImageConfiguration, are stored - /// in the registry as plain blobs with MIME type "application/octet-stream". - /// This function attempts to decode the received data without reference - /// to the MIME type. - func getBlob(repository: ImageReference.Repository, digest: ImageReference.Digest) async throws - -> Response - { - let (data, _) = try await executeRequestThrowing( - .get(repository, path: "blobs/\(digest)", accepting: ["application/octet-stream"]), - decodingErrors: [.notFound] - ) - return try decoder.decode(Response.self, from: data) - } - /// Uploads a blob to the registry. /// /// This function uploads a blob of unstructured data to the registry. diff --git a/Sources/ContainerRegistry/RegistryClient+ImageConfiguration.swift b/Sources/ContainerRegistry/RegistryClient+ImageConfiguration.swift index dc91cf3..02a8d11 100644 --- a/Sources/ContainerRegistry/RegistryClient+ImageConfiguration.swift +++ b/Sources/ContainerRegistry/RegistryClient+ImageConfiguration.swift @@ -21,10 +21,12 @@ extension RegistryClient { /// - Throws: If the blob cannot be decoded as an `ImageConfiguration`. /// /// Image configuration records are stored as blobs in the registry. This function retrieves the requested blob and tries to decode it as a configuration record. - public func getImageConfiguration(forImage image: ImageReference, digest: ImageReference.Digest) async throws - -> ImageConfiguration - { - try await getBlob(repository: image.repository, digest: digest) + public func getImageConfiguration( + forImage image: ImageReference, + digest: ImageReference.Digest + ) async throws -> ImageConfiguration { + let data = try await getBlob(repository: image.repository, digest: digest) + return try decoder.decode(ImageConfiguration.self, from: data) } /// Upload an image configuration record to the registry. @@ -35,9 +37,10 @@ extension RegistryClient { /// - Throws: If the blob upload fails. /// /// Image configuration records are stored as blobs in the registry. This function encodes the provided configuration record and stores it as a blob in the registry. - public func putImageConfiguration(forImage image: ImageReference, configuration: ImageConfiguration) async throws - -> ContentDescriptor - { + public func putImageConfiguration( + forImage image: ImageReference, + configuration: ImageConfiguration + ) async throws -> ContentDescriptor { try await putBlob( repository: image.repository, mediaType: "application/vnd.oci.image.config.v1+json",