Skip to content

Commit 24e5d8a

Browse files
committed
update registry publishing
motivation: update based on recent spec changes: * do not request server requirments, deduce configuration based on use provided flags * fix issues in publishing logic per testing
1 parent 4582d47 commit 24e5d8a

File tree

4 files changed

+146
-130
lines changed

4 files changed

+146
-130
lines changed

Sources/PackageRegistry/RegistryClient.swift

Lines changed: 104 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -648,73 +648,74 @@ public final class RegistryClient: Cancellable {
648648
}
649649
}
650650

651-
public func getPublishRequirements(
652-
registryURL: URL,
653-
timeout: DispatchTimeInterval? = .none,
654-
observabilityScope: ObservabilityScope,
655-
callbackQueue: DispatchQueue,
656-
completion: @escaping (Result<PublishRequirements, Error>) -> Void
657-
) {
658-
let completion = self.makeAsync(completion, on: callbackQueue)
659-
660-
guard var components = URLComponents(url: registryURL, resolvingAgainstBaseURL: true) else {
661-
return completion(.failure(RegistryError.invalidURL(registryURL)))
662-
}
663-
components.appendPathComponents("publish-requirements")
664-
guard let url = components.url else {
665-
return completion(.failure(RegistryError.invalidURL(registryURL)))
666-
}
667-
668-
let request = LegacyHTTPClient.Request(
669-
method: .get,
670-
url: url,
671-
headers: [
672-
"Accept": self.acceptHeader(mediaType: .json),
673-
],
674-
options: self.defaultRequestOptions(timeout: timeout, callbackQueue: callbackQueue)
675-
)
676-
677-
self.httpClient.execute(request, observabilityScope: observabilityScope, progress: nil) { result in
678-
completion(
679-
result.tryMap { response in
680-
switch response.statusCode {
681-
case 200:
682-
let publishRequirements = try response.parseJSON(
683-
Serialization.PublishRequirements.self,
684-
decoder: self.jsonDecoder
685-
)
686-
687-
return PublishRequirements(
688-
metadata: .init(
689-
location: publishRequirements.metadata.location.map {
690-
switch $0 {
691-
case .archive:
692-
return .archive
693-
case .request:
694-
return .request
695-
}
696-
}
697-
),
698-
signing: .init(
699-
required: publishRequirements.signing.required,
700-
acceptedSignatureFormats: publishRequirements.signing.acceptedSignatureFormats.map {
701-
switch $0 {
702-
case .CMS_1_0_0:
703-
return .CMS_1_0_0
704-
}
705-
},
706-
trustedRootCertificates: publishRequirements.signing.trustedRootCertificates
707-
)
708-
)
709-
default:
710-
throw self.unexpectedStatusError(response, expectedStatus: [200])
711-
}
712-
}.mapError {
713-
RegistryError.failedRetrievingRegistryPublishRequirements($0)
714-
}
715-
)
716-
}
717-
}
651+
/*
652+
public func getPublishRequirements(
653+
registryURL: URL,
654+
timeout: DispatchTimeInterval? = .none,
655+
observabilityScope: ObservabilityScope,
656+
callbackQueue: DispatchQueue,
657+
completion: @escaping (Result<PublishRequirements, Error>) -> Void
658+
) {
659+
let completion = self.makeAsync(completion, on: callbackQueue)
660+
661+
guard var components = URLComponents(url: registryURL, resolvingAgainstBaseURL: true) else {
662+
return completion(.failure(RegistryError.invalidURL(registryURL)))
663+
}
664+
components.appendPathComponents("publish-requirements")
665+
guard let url = components.url else {
666+
return completion(.failure(RegistryError.invalidURL(registryURL)))
667+
}
668+
669+
let request = LegacyHTTPClient.Request(
670+
method: .get,
671+
url: url,
672+
headers: [
673+
"Accept": self.acceptHeader(mediaType: .json),
674+
],
675+
options: self.defaultRequestOptions(timeout: timeout, callbackQueue: callbackQueue)
676+
)
677+
678+
self.httpClient.execute(request, observabilityScope: observabilityScope, progress: nil) { result in
679+
completion(
680+
result.tryMap { response in
681+
switch response.statusCode {
682+
case 200:
683+
let publishRequirements = try response.parseJSON(
684+
Serialization.PublishRequirements.self,
685+
decoder: self.jsonDecoder
686+
)
687+
688+
return PublishRequirements(
689+
metadata: .init(
690+
location: publishRequirements.metadata.location.map {
691+
switch $0 {
692+
case .archive:
693+
return .archive
694+
case .request:
695+
return .request
696+
}
697+
}
698+
),
699+
signing: .init(
700+
required: publishRequirements.signing.required,
701+
acceptedSignatureFormats: publishRequirements.signing.acceptedSignatureFormats.map {
702+
switch $0 {
703+
case .CMS_1_0_0:
704+
return .CMS_1_0_0
705+
}
706+
},
707+
trustedRootCertificates: publishRequirements.signing.trustedRootCertificates
708+
)
709+
)
710+
default:
711+
throw self.unexpectedStatusError(response, expectedStatus: [200])
712+
}
713+
}.mapError {
714+
RegistryError.failedRetrievingRegistryPublishRequirements($0)
715+
}
716+
)
717+
}
718+
}*/
718719

719720
public func publish(
720721
registryURL: URL,
@@ -759,43 +760,46 @@ public final class RegistryClient: Cancellable {
759760
}
760761

761762
// TODO: add generic support for upload requests in Basics
762-
var body = Data()
763763
let boundary = UUID().uuidString
764+
var parts = [String]()
764765

765766
// archive field
766-
body.append(contentsOf: """
767-
--\(boundary)
767+
parts.append("""
768768
Content-Disposition: form-data; name=\"source-archive\"\r
769769
Content-Type: application/zip\r
770770
Content-Transfer-Encoding: base64\r
771771
Content-Length: \(packageArchiveContent.count)\r
772772
\r
773-
\(packageArchiveContent.base64EncodedString())
774-
""".utf8)
773+
\(packageArchiveContent.base64EncodedString())\r
774+
""")
775775

776776
if let metadataContent = metadataContent {
777-
// spacer
778-
body.append(contentsOf: "\r\n".utf8)
779-
// metadata fiels
780-
body.append(contentsOf: """
781-
--\(boundary)\r
777+
parts.append("""
782778
Content-Disposition: form-data; name=\"metadata\"\r
783779
Content-Type: application/json\r
784780
Content-Transfer-Encoding: quoted-printable\r
785781
Content-Length: \(metadataContent.count)\r
786782
\r
787-
\(metadataContent)
788-
""".utf8)
783+
\(metadataContent)\r
784+
""")
789785
}
790786

787+
let bodyString = """
788+
--\(boundary)\r
789+
\(parts.joined(separator: "\n--\(boundary)\r\n"))
790+
--\(boundary)--\r\n
791+
"""
792+
791793
let request = LegacyHTTPClient.Request(
792794
method: .put,
793795
url: url,
794796
headers: [
797+
"Content-Type": "multipart/form-data;boundary=\"\(boundary)\"",
795798
"Accept": self.acceptHeader(mediaType: .json),
799+
"Expect": "100-continue",
796800
"Prefer": "respond-async",
797801
],
798-
body: body,
802+
body: Data(bodyString.utf8),
799803
options: self.defaultRequestOptions(timeout: timeout, callbackQueue: callbackQueue)
800804
)
801805

@@ -1024,12 +1028,21 @@ extension RegistryClient {
10241028
}
10251029

10261030
extension RegistryClient {
1027-
public struct PublishRequirements {
1031+
public struct PublishConfiguration {
10281032
public let metadata: Metadata
10291033
public let signing: Signing
10301034

1035+
public init(metadata: Metadata, signing: Signing) {
1036+
self.metadata = metadata
1037+
self.signing = signing
1038+
}
1039+
10311040
public struct Metadata {
10321041
public let location: [MetadataLocation]
1042+
1043+
public init(location: [MetadataLocation]) {
1044+
self.location = location
1045+
}
10331046
}
10341047

10351048
public enum MetadataLocation {
@@ -1041,6 +1054,16 @@ extension RegistryClient {
10411054
public let required: Bool
10421055
public let acceptedSignatureFormats: [SignatureFormat]
10431056
public let trustedRootCertificates: [String]
1057+
1058+
public init(
1059+
required: Bool,
1060+
acceptedSignatureFormats: [SignatureFormat],
1061+
trustedRootCertificates: [String]
1062+
) {
1063+
self.required = required
1064+
self.acceptedSignatureFormats = acceptedSignatureFormats
1065+
self.trustedRootCertificates = trustedRootCertificates
1066+
}
10441067
}
10451068
}
10461069
}

Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ extension SwiftPackageRegistryTool {
3333
@OptionGroup(visibility: .hidden)
3434
var globalOptions: GlobalOptions
3535

36-
@Option(name: .customLong("id"), help: "The package identity")
36+
@Option(name: [.customLong("id"), .customLong("package-id")] , help: "The package identity")
3737
var packageIdentity: PackageIdentity
3838

39-
@Option(name: .customLong("version"), help: "The package version")
39+
@Option(name: [.customLong("version"), .customLong("package-version")], help: "The package version")
4040
var packageVersion: Version
4141

42-
@Option(name: .customLong("url"), help: "The registry URL")
42+
@Option(name: [.customLong("url"), .customLong("registry-url")], help: "The registry URL")
4343
var registryURL: URL?
4444

4545
@Option(
@@ -66,7 +66,7 @@ extension SwiftPackageRegistryTool {
6666
@Option(
6767
help: "Paths to all of the certificates (DER-encoded) in the chain. The certificate used for signing must be listed first and the root certificate last."
6868
)
69-
var certificateChainPaths: [AbsolutePath]
69+
var certificateChainPaths: [AbsolutePath] = []
7070

7171
func run(_ swiftTool: SwiftTool) throws {
7272
let configuration = try getRegistriesConfig(swiftTool).configuration
@@ -106,6 +106,12 @@ extension SwiftPackageRegistryTool {
106106
}
107107
}
108108

109+
let workingDirectory = customWorkingDirectory ?? Workspace.DefaultLocations
110+
.scratchDirectory(forRootPackage: packageDirectory).appending(components: ["registry", "publish"])
111+
if localFileSystem.exists(workingDirectory) {
112+
try localFileSystem.removeFileTree(workingDirectory)
113+
}
114+
109115
// validate custom metadata path
110116
if let customMetadataPath = self.customMetadataPath {
111117
guard localFileSystem.exists(customMetadataPath) else {
@@ -125,6 +131,7 @@ extension SwiftPackageRegistryTool {
125131
)
126132

127133
// step 1: get registry publishing requirements
134+
/*
128135
swiftTool.observabilityScope.emit(info: "retrieving '\(registryURL)' publishing requirements")
129136
let publishRequirements = try tsc_await { callback in
130137
registryClient.getPublishRequirements(
@@ -133,7 +140,15 @@ extension SwiftPackageRegistryTool {
133140
callbackQueue: .sharedConcurrent,
134141
completion: callback
135142
)
136-
}
143+
}*/
144+
let publishConfiguration = RegistryClient.PublishConfiguration(
145+
metadata: .init(location: [.archive, .request]), // FIXME
146+
signing: .init(
147+
required: self.signingIdentity != nil || self.privateKeyPath != nil,
148+
acceptedSignatureFormats: [.CMS_1_0_0],
149+
trustedRootCertificates: []
150+
)
151+
)
137152

138153
// step 2: generate source archive for the package release
139154
let metadataPath = self.customMetadataPath ?? packageDirectory.appending(component: Self.metadataFilename)
@@ -148,15 +163,15 @@ extension SwiftPackageRegistryTool {
148163
packageIdentity: self.packageIdentity,
149164
packageVersion: self.packageVersion,
150165
packageDirectory: packageDirectory,
151-
metadataPath: publishRequirements.metadata.location.contains(.archive) ? metadataPath : .none,
152-
customWorkingDirectory: self.customWorkingDirectory,
166+
metadataPath: publishConfiguration.metadata.location.contains(.archive) ? metadataPath : .none,
167+
workingDirectory: workingDirectory,
153168
cancellator: swiftTool.cancellator,
154169
observabilityScope: swiftTool.observabilityScope
155170
)
156171

157172
// step 3: sign the source archive if needed
158173
var signature: Data? = .none
159-
if publishRequirements.signing.required {
174+
if publishConfiguration.signing.required {
160175
swiftTool.observabilityScope.emit(info: "signing the archive at '\(archivePath)'")
161176
signature = try self.sign(
162177
archivePath: archivePath,
@@ -177,7 +192,7 @@ extension SwiftPackageRegistryTool {
177192
packageIdentity: self.packageIdentity,
178193
packageVersion: self.packageVersion,
179194
packageArchive: archivePath,
180-
packageMetadata: publishRequirements.metadata.location.contains(.request) ? metadataPath : .none,
195+
packageMetadata: publishConfiguration.metadata.location.contains(.request) ? metadataPath : .none,
181196
signature: signature,
182197
fileSystem: localFileSystem,
183198
observabilityScope: swiftTool.observabilityScope,
@@ -201,13 +216,10 @@ extension SwiftPackageRegistryTool {
201216
packageVersion: Version,
202217
packageDirectory: AbsolutePath,
203218
metadataPath: AbsolutePath?,
204-
customWorkingDirectory: AbsolutePath?,
219+
workingDirectory: AbsolutePath,
205220
cancellator: Cancellator?,
206221
observabilityScope: ObservabilityScope
207222
) throws -> AbsolutePath {
208-
let workingDirectory = customWorkingDirectory ?? Workspace.DefaultLocations
209-
.scratchDirectory(forRootPackage: packageDirectory).appending(components: ["registry", "publish"])
210-
211223
let archivePath = workingDirectory.appending(component: "\(packageIdentity)-\(packageVersion).zip")
212224

213225
// create temp location for sources

0 commit comments

Comments
 (0)