From 068fa499cfeab10fe23cb66a105f89b01d9889d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Tue, 18 Jul 2023 15:09:30 -0700 Subject: [PATCH] Transition away from `Foundation.URL` (#6706) The parser of `NSURL` is changing in macOS Sonoma and will no longer be compatible with the GitHub-style SSH URLs which means we have to transition back to using our own URL type (which is a wrapper of `String` for now) in order to continue to support SSH URLs. rdar://112482783 --- Sources/Basics/CMakeLists.txt | 1 + Sources/Basics/SourceControlURL.swift | 53 +++++++++++++++++++ .../PackageCollections+Validation.swift | 2 +- .../GitHubPackageMetadataProvider.swift | 4 +- .../JSONPackageCollectionProvider.swift | 2 +- .../FilePackageFingerprintStorage.swift | 9 ++-- Sources/PackageFingerprint/Model.swift | 7 +-- Sources/PackageGraph/DependencyMirrors.swift | 4 +- Sources/PackageGraph/PinsStore.swift | 9 +--- .../PackageLoading/ManifestJSONParser.swift | 11 ++-- ...RegistryReleaseMetadataSerialization.swift | 2 +- Sources/PackageMetadata/PackageMetadata.swift | 30 +++++++---- Sources/PackageModel/IdentityResolver.swift | 12 ++--- .../PackageDependencyDescription.swift | 4 +- Sources/PackageModel/PackageIdentity.swift | 4 +- Sources/PackageModel/PackageReference.swift | 4 +- .../RegistryReleaseMetadata.swift | 5 +- Sources/PackageRegistry/RegistryClient.swift | 19 ++++--- .../SPMTestSupport/ManifestExtensions.swift | 2 +- Sources/SPMTestSupport/MockDependency.swift | 16 +++--- Sources/SPMTestSupport/MockPackage.swift | 4 +- Sources/SPMTestSupport/MockWorkspace.swift | 2 +- ...ckageDependencyDescriptionExtensions.swift | 2 +- Sources/SourceControl/GitRepository.swift | 6 +-- Sources/SourceControl/Repository.swift | 19 ++++--- Sources/Workspace/Workspace+State.swift | 14 ++--- Sources/Workspace/Workspace.swift | 4 +- .../GitHubPackageMetadataProviderTests.swift | 18 +++---- .../PackageIndexTests.swift | 12 ++--- .../FilePackageFingerprintStorageTests.swift | 24 ++++----- Tests/PackageGraphTests/PubgrubTests.swift | 2 +- .../PD_5_6_LoadingTests.swift | 3 +- .../TargetSourcesBuilderTests.swift | 6 +-- .../PackageVersionChecksumTOFUTests.swift | 6 +-- .../RegistryClientTests.swift | 34 ++++++------ .../RepositoryManagerTests.swift | 8 +-- Tests/WorkspaceTests/PinsStoreTests.swift | 20 +++---- .../SourceControlPackageContainerTests.swift | 2 +- Tests/WorkspaceTests/WorkspaceTests.swift | 2 +- 39 files changed, 218 insertions(+), 170 deletions(-) create mode 100644 Sources/Basics/SourceControlURL.swift diff --git a/Sources/Basics/CMakeLists.txt b/Sources/Basics/CMakeLists.txt index bb0b93886fc..5a6c948e874 100644 --- a/Sources/Basics/CMakeLists.txt +++ b/Sources/Basics/CMakeLists.txt @@ -31,6 +31,7 @@ add_library(Basics FileSystem/TemporaryFile.swift FileSystem/TSCAdapters.swift FileSystem/VFSOverlay.swift + SourceControlURL.swift HTTPClient/HTTPClient.swift HTTPClient/HTTPClientConfiguration.swift HTTPClient/HTTPClientError.swift diff --git a/Sources/Basics/SourceControlURL.swift b/Sources/Basics/SourceControlURL.swift new file mode 100644 index 00000000000..398c2f89ddf --- /dev/null +++ b/Sources/Basics/SourceControlURL.swift @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Foundation + +public struct SourceControlURL: Codable, Equatable, Hashable, Sendable { + private let urlString: String + + public init(stringLiteral: String) { + self.urlString = stringLiteral + } + + public init(_ urlString: String) { + self.urlString = urlString + } + + public init(_ url: URL) { + self.urlString = url.absoluteString + } + + public var absoluteString: String { + return self.urlString + } + + public var lastPathComponent: String { + return (self.urlString as NSString).lastPathComponent + } + + public var url: URL? { + return URL(string: self.urlString) + } +} + +extension SourceControlURL: CustomStringConvertible { + public var description: String { + return self.urlString + } +} + +extension SourceControlURL: ExpressibleByStringInterpolation { +} + +extension SourceControlURL: ExpressibleByStringLiteral { +} diff --git a/Sources/PackageCollections/PackageCollections+Validation.swift b/Sources/PackageCollections/PackageCollections+Validation.swift index fb088a480b4..3940021555d 100644 --- a/Sources/PackageCollections/PackageCollections+Validation.swift +++ b/Sources/PackageCollections/PackageCollections+Validation.swift @@ -82,7 +82,7 @@ extension PackageCollectionModel.V1 { // TODO: validate package url? private func validate(package: Collection.Package, messages: inout [ValidationMessage]) { - let packageID = "\(PackageIdentity(url: package.url).description) (\(package.url.absoluteString))" + let packageID = "\(PackageIdentity(url: SourceControlURL(package.url)).description) (\(package.url.absoluteString))" guard !package.versions.isEmpty else { messages.append(.error("Package \(packageID) does not have any versions.", property: "package.versions")) diff --git a/Sources/PackageCollections/Providers/GitHubPackageMetadataProvider.swift b/Sources/PackageCollections/Providers/GitHubPackageMetadataProvider.swift index c7219d8aa9a..ad32f77b2e2 100644 --- a/Sources/PackageCollections/Providers/GitHubPackageMetadataProvider.swift +++ b/Sources/PackageCollections/Providers/GitHubPackageMetadataProvider.swift @@ -62,7 +62,7 @@ struct GitHubPackageMetadataProvider: PackageMetadataProvider, Closable { callback: @escaping (Result, PackageMetadataProviderContext?) -> Void ) { guard let baseURL = Self.apiURL(location) else { - return self.errorCallback(GitHubPackageMetadataProviderError.invalidGitURL(location), apiHost: nil, callback: callback) + return self.errorCallback(GitHubPackageMetadataProviderError.invalidSourceControlURL(location), apiHost: nil, callback: callback) } if let cached = try? self.cache?.get(key: identity.description) { @@ -334,7 +334,7 @@ struct GitHubPackageMetadataProvider: PackageMetadataProvider, Closable { } enum GitHubPackageMetadataProviderError: Error, Equatable { - case invalidGitURL(String) + case invalidSourceControlURL(String) case invalidResponse(URL, String) case permissionDenied(URL) case invalidAuthToken(URL) diff --git a/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift b/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift index 615b074537c..db69805f276 100644 --- a/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift +++ b/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift @@ -371,7 +371,7 @@ struct JSONPackageCollectionProvider: PackageCollectionProvider { // If package identity is set, use that. Otherwise create one from URL. return .init( - identity: package.identity.map { PackageIdentity.plain($0) } ?? PackageIdentity(url: package.url), + identity: package.identity.map { PackageIdentity.plain($0) } ?? PackageIdentity(url: SourceControlURL(package.url)), location: package.url.absoluteString, summary: package.summary, keywords: package.keywords, diff --git a/Sources/PackageFingerprint/FilePackageFingerprintStorage.swift b/Sources/PackageFingerprint/FilePackageFingerprintStorage.swift index 918a6594b66..da7975ef72a 100644 --- a/Sources/PackageFingerprint/FilePackageFingerprintStorage.swift +++ b/Sources/PackageFingerprint/FilePackageFingerprintStorage.swift @@ -265,15 +265,14 @@ private enum StorageModel { let fingerprintsByContentType = try Dictionary( throwingUniqueKeysWithValues: fingerprintsForKind.map { _, storedFingerprint in - guard let originURL = URL(string: storedFingerprint.origin) else { - throw SerializationError.invalidURL(storedFingerprint.origin) - } - let origin: Fingerprint.Origin switch kind { case .sourceControl: - origin = .sourceControl(originURL) + origin = .sourceControl(SourceControlURL(storedFingerprint.origin)) case .registry: + guard let originURL = URL(string: storedFingerprint.origin) else { + throw SerializationError.invalidURL(storedFingerprint.origin) + } origin = .registry(originURL) } diff --git a/Sources/PackageFingerprint/Model.swift b/Sources/PackageFingerprint/Model.swift index 7575bd42009..affe35f05b0 100644 --- a/Sources/PackageFingerprint/Model.swift +++ b/Sources/PackageFingerprint/Model.swift @@ -12,6 +12,7 @@ import struct Foundation.URL +import Basics import PackageModel import struct TSCUtility.Version @@ -34,7 +35,7 @@ extension Fingerprint { } public enum Origin: Equatable, CustomStringConvertible { - case sourceControl(URL) + case sourceControl(SourceControlURL) case registry(URL) public var kind: Fingerprint.Kind { @@ -46,12 +47,12 @@ extension Fingerprint { } } - public var url: URL? { + public var url: SourceControlURL? { switch self { case .sourceControl(let url): return url case .registry(let url): - return url + return SourceControlURL(url.absoluteString) } } diff --git a/Sources/PackageGraph/DependencyMirrors.swift b/Sources/PackageGraph/DependencyMirrors.swift index 51780e3a015..6ba162d8664 100644 --- a/Sources/PackageGraph/DependencyMirrors.swift +++ b/Sources/PackageGraph/DependencyMirrors.swift @@ -166,10 +166,8 @@ public final class DependencyMirrors: Equatable { return PackageIdentity.plain(location) } else if let path = try? AbsolutePath(validating: location) { return PackageIdentity(path: path) - } else if let url = URL(string: location) { - return PackageIdentity(url: url) } else { - throw StringError("invalid location \(location), cannot extract identity") + return PackageIdentity(url: SourceControlURL(location)) } } } diff --git a/Sources/PackageGraph/PinsStore.swift b/Sources/PackageGraph/PinsStore.swift index df738c04882..3a17e4b47ad 100644 --- a/Sources/PackageGraph/PinsStore.swift +++ b/Sources/PackageGraph/PinsStore.swift @@ -428,10 +428,8 @@ extension PinsStore.Pin { var packageRef: PackageReference if let path = try? AbsolutePath(validating: location) { packageRef = .localSourceControl(identity: identity, path: path) - } else if let url = URL(string: location) { - packageRef = .remoteSourceControl(identity: identity, url: url) } else { - throw StringError("invalid package location \(location)") + packageRef = .remoteSourceControl(identity: identity, url: SourceControlURL(location)) } if let newName = pin.package { packageRef = packageRef.withName(newName) @@ -466,10 +464,7 @@ extension PinsStore.Pin { case .localSourceControl: packageRef = try .localSourceControl(identity: identity, path: AbsolutePath(validating: location)) case .remoteSourceControl: - guard let url = URL(string: location) else { - throw StringError("invalid url location: \(location)") - } - packageRef = .remoteSourceControl(identity: identity, url: url) + packageRef = .remoteSourceControl(identity: identity, url: SourceControlURL(location)) case .registry: packageRef = .registry(identity: identity) } diff --git a/Sources/PackageLoading/ManifestJSONParser.swift b/Sources/PackageLoading/ManifestJSONParser.swift index b23b91ff710..2e5bc5af636 100644 --- a/Sources/PackageLoading/ManifestJSONParser.swift +++ b/Sources/PackageLoading/ManifestJSONParser.swift @@ -15,6 +15,7 @@ import PackageModel import struct Basics.AbsolutePath import protocol Basics.FileSystem +import struct Basics.SourceControlURL import struct Basics.InternalError import struct Basics.RelativePath @@ -237,7 +238,8 @@ enum ManifestJSONParser { requirement: requirement, productFilter: .everything ) - } else if let url = URL(string: location){ + } else { + let url = SourceControlURL(location) // in the future this will check with the registries for the identity of the URL let identity = try identityResolver.resolveIdentity(for: url) return .remoteSourceControl( @@ -247,8 +249,6 @@ enum ManifestJSONParser { requirement: requirement, productFilter: .everything ) - } else { - throw StringError("invalid location: \(location)") } } @@ -268,8 +268,9 @@ enum ManifestJSONParser { productFilter: .everything ) } else if let url = URL(string: location){ + let SourceControlURL = SourceControlURL(url) // in the future this will check with the registries for the identity of the URL - let identity = try identityResolver.resolveIdentity(for: url) + let identity = try identityResolver.resolveIdentity(for: SourceControlURL) let sourceControlRequirement: PackageDependency.SourceControl.Requirement switch requirement { case .exact(let value): @@ -280,7 +281,7 @@ enum ManifestJSONParser { return .remoteSourceControl( identity: identity, nameForTargetDependencyResolutionOnly: identity.description, - url: url, + url: SourceControlURL, requirement: sourceControlRequirement, productFilter: .everything ) diff --git a/Sources/PackageLoading/RegistryReleaseMetadataSerialization.swift b/Sources/PackageLoading/RegistryReleaseMetadataSerialization.swift index f892019b626..451e29bd286 100644 --- a/Sources/PackageLoading/RegistryReleaseMetadataSerialization.swift +++ b/Sources/PackageLoading/RegistryReleaseMetadataSerialization.swift @@ -43,7 +43,7 @@ private struct CodableRegistryReleaseMetadata: Codable { public let description: String? public let licenseURL: URL? public let readmeURL: URL? - public let scmRepositoryURLs: [URL]? + public let scmRepositoryURLs: [SourceControlURL]? init(_ seed: RegistryReleaseMetadata) { switch seed.source { diff --git a/Sources/PackageMetadata/PackageMetadata.swift b/Sources/PackageMetadata/PackageMetadata.swift index 02c5cd2e778..5435ecb47a3 100644 --- a/Sources/PackageMetadata/PackageMetadata.swift +++ b/Sources/PackageMetadata/PackageMetadata.swift @@ -27,7 +27,7 @@ public struct Package { public enum Source { case indexAndCollections(collections: [PackageCollectionsModel.CollectionIdentifier], indexes: [URL]) case registry(url: URL) - case sourceControl(url: URL) + case sourceControl(url: SourceControlURL) } public struct Resource: Sendable { @@ -67,7 +67,7 @@ public struct Package { // Per version metadata based on the latest version that we include here for convenience. public let licenseURL: URL? public let readmeURL: URL? - public let repositoryURLs: [URL]? + public let repositoryURLs: [SourceControlURL]? public let resources: [Resource] public let author: Author? public let description: String? @@ -82,7 +82,7 @@ public struct Package { versions: [Version], licenseURL: URL? = nil, readmeURL: URL? = nil, - repositoryURLs: [URL]?, + repositoryURLs: [SourceControlURL]?, resources: [Resource], author: Author?, description: String?, @@ -133,11 +133,21 @@ public struct PackageSearchClient { } // FIXME: This matches the current implementation, but we may want be smarter about it? + private func guessReadMeURL(baseURL: SourceControlURL, defaultBranch: String) -> URL? { + if let baseURL = baseURL.url { + return guessReadMeURL(baseURL: baseURL, defaultBranch: defaultBranch) + } else { + return nil + } + } + private func guessReadMeURL(baseURL: URL, defaultBranch: String) -> URL { baseURL.appendingPathComponent("raw").appendingPathComponent(defaultBranch).appendingPathComponent("README.md") } - private func guessReadMeURL(alternateLocations: [URL]?) -> URL? { + + + private func guessReadMeURL(alternateLocations: [SourceControlURL]?) -> URL? { if let alternateURL = alternateLocations?.first { // FIXME: This is pretty crude, we should let the registry metadata provide the value instead. return guessReadMeURL(baseURL: alternateURL, defaultBranch: "main") @@ -148,7 +158,7 @@ public struct PackageSearchClient { private struct Metadata { public let licenseURL: URL? public let readmeURL: URL? - public let repositoryURLs: [URL]? + public let repositoryURLs: [SourceControlURL]? public let resources: [Package.Resource] public let author: Package.Author? public let description: String? @@ -231,9 +241,7 @@ public struct PackageSearchClient { // as a URL or there are any errors during the process, we fall back to searching the configured // index or package collections. let fetchStandalonePackageByURL = { (error: Error?) in - guard let url = URL(string: query) else { - return search(error) - } + let url = SourceControlURL(query) do { try withTemporaryDirectory(removeTreeOnDeinit: true) { (tempDir: AbsolutePath) in @@ -303,7 +311,7 @@ public struct PackageSearchClient { self.getVersionMetadata(package: identity, version: version) { result in let licenseURL: URL? let readmeURL: URL? - let repositoryURLs: [URL]? + let repositoryURLs: [SourceControlURL]? let resources: [Package.Resource] let author: Package.Author? let description: String? @@ -372,7 +380,7 @@ public struct PackageSearchClient { } public func lookupIdentities( - scmURL: URL, + scmURL: SourceControlURL, timeout: DispatchTimeInterval? = .none, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, @@ -392,7 +400,7 @@ public struct PackageSearchClient { timeout: DispatchTimeInterval? = .none, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, - completion: @escaping (Result, Error>) -> Void + completion: @escaping (Result, Error>) -> Void ) { registryClient.getPackageMetadata( package: package, diff --git a/Sources/PackageModel/IdentityResolver.swift b/Sources/PackageModel/IdentityResolver.swift index a03d0a04828..847836ecf2d 100644 --- a/Sources/PackageModel/IdentityResolver.swift +++ b/Sources/PackageModel/IdentityResolver.swift @@ -16,7 +16,7 @@ import Foundation // TODO: refactor this when adding registry support public protocol IdentityResolver { func resolveIdentity(for packageKind: PackageReference.Kind) throws -> PackageIdentity - func resolveIdentity(for url: URL) throws -> PackageIdentity + func resolveIdentity(for url: SourceControlURL) throws -> PackageIdentity func resolveIdentity(for path: AbsolutePath) throws -> PackageIdentity func mappedLocation(for location: String) -> String func mappedIdentity(for identity: PackageIdentity) throws -> PackageIdentity @@ -49,14 +49,12 @@ public struct DefaultIdentityResolver: IdentityResolver { } } - public func resolveIdentity(for url: URL) throws -> PackageIdentity { + public func resolveIdentity(for url: SourceControlURL) throws -> PackageIdentity { let location = self.mappedLocation(for: url.absoluteString) if let path = try? AbsolutePath(validating: location) { return PackageIdentity(path: path) - } else if let url = URL(string: location) { - return PackageIdentity(url: url) } else { - throw StringError("invalid mapped location: \(location) for \(url)") + return PackageIdentity(url: SourceControlURL(location)) } } @@ -64,10 +62,8 @@ public struct DefaultIdentityResolver: IdentityResolver { let location = self.mappedLocation(for: path.pathString) if let path = try? AbsolutePath(validating: location) { return PackageIdentity(path: path) - } else if let url = URL(string: location) { - return PackageIdentity(url: url) } else { - throw StringError("invalid mapped location: \(location) for \(path)") + return PackageIdentity(url: SourceControlURL(location)) } } diff --git a/Sources/PackageModel/Manifest/PackageDependencyDescription.swift b/Sources/PackageModel/Manifest/PackageDependencyDescription.swift index ef17ec8fb83..39fd89df3a0 100644 --- a/Sources/PackageModel/Manifest/PackageDependencyDescription.swift +++ b/Sources/PackageModel/Manifest/PackageDependencyDescription.swift @@ -46,7 +46,7 @@ public enum PackageDependency: Equatable, Hashable, Sendable { public enum Location: Equatable, Hashable, Sendable { case local(AbsolutePath) - case remote(URL) + case remote(SourceControlURL) } } @@ -173,7 +173,7 @@ public enum PackageDependency: Equatable, Hashable, Sendable { public static func remoteSourceControl(identity: PackageIdentity, nameForTargetDependencyResolutionOnly: String?, - url: URL, + url: SourceControlURL, requirement: SourceControl.Requirement, productFilter: ProductFilter ) -> Self { diff --git a/Sources/PackageModel/PackageIdentity.swift b/Sources/PackageModel/PackageIdentity.swift index 3066fdf56d9..327681b3df2 100644 --- a/Sources/PackageModel/PackageIdentity.swift +++ b/Sources/PackageModel/PackageIdentity.swift @@ -26,7 +26,7 @@ public struct PackageIdentity: CustomStringConvertible, Sendable { /// Creates a package identity from a URL. /// - Parameter url: The package's URL. - public init(url: URL) { + public init(url: SourceControlURL) { self.init(urlString: url.absoluteString) } @@ -305,7 +305,7 @@ struct PackageIdentityParser { } /// Compute the default name of a package given its URL. - public static func computeDefaultName(fromURL url: URL) -> String { + public static func computeDefaultName(fromURL url: SourceControlURL) -> String { Self.computeDefaultName(fromLocation: url.absoluteString) } diff --git a/Sources/PackageModel/PackageReference.swift b/Sources/PackageModel/PackageReference.swift index 6238eae42ae..fa2f4049a46 100644 --- a/Sources/PackageModel/PackageReference.swift +++ b/Sources/PackageModel/PackageReference.swift @@ -29,7 +29,7 @@ public struct PackageReference { case localSourceControl(AbsolutePath) /// A remote source package. - case remoteSourceControl(URL) + case remoteSourceControl(SourceControlURL) /// A package from a registry. case registry(PackageIdentity) @@ -144,7 +144,7 @@ public struct PackageReference { PackageReference(identity: identity, kind: .localSourceControl(path)) } - public static func remoteSourceControl(identity: PackageIdentity, url: URL) -> PackageReference { + public static func remoteSourceControl(identity: PackageIdentity, url: SourceControlURL) -> PackageReference { PackageReference(identity: identity, kind: .remoteSourceControl(url)) } diff --git a/Sources/PackageModel/RegistryReleaseMetadata.swift b/Sources/PackageModel/RegistryReleaseMetadata.swift index c77018eafe8..f0e1affc2e9 100644 --- a/Sources/PackageModel/RegistryReleaseMetadata.swift +++ b/Sources/PackageModel/RegistryReleaseMetadata.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import struct Basics.SourceControlURL import struct Foundation.URL import struct TSCUtility.Version @@ -35,14 +36,14 @@ public struct RegistryReleaseMetadata { public let description: String? public let licenseURL: URL? public let readmeURL: URL? - public let scmRepositoryURLs: [URL]? + public let scmRepositoryURLs: [SourceControlURL]? public init( author: RegistryReleaseMetadata.Metadata.Author? = nil, description: String? = nil, licenseURL: URL? = nil, readmeURL: URL? = nil, - scmRepositoryURLs: [URL]? + scmRepositoryURLs: [SourceControlURL]? ) { self.author = author self.description = description diff --git a/Sources/PackageRegistry/RegistryClient.swift b/Sources/PackageRegistry/RegistryClient.swift index 46e55035e9e..a267ddf75c8 100644 --- a/Sources/PackageRegistry/RegistryClient.swift +++ b/Sources/PackageRegistry/RegistryClient.swift @@ -336,7 +336,7 @@ public final class RegistryClient: Cancellable { registry: registry, licenseURL: versionMetadata.metadata?.licenseURL.flatMap { URL(string: $0) }, readmeURL: versionMetadata.metadata?.readmeURL.flatMap { URL(string: $0) }, - repositoryURLs: versionMetadata.metadata?.repositoryURLs?.compactMap { URL(string: $0) }, + repositoryURLs: versionMetadata.metadata?.repositoryURLs?.compactMap { SourceControlURL($0) }, resources: versionMetadata.resources.map { .init( name: $0.name, @@ -1197,7 +1197,7 @@ public final class RegistryClient: Cancellable { } public func lookupIdentities( - scmURL: URL, + scmURL: SourceControlURL, timeout: DispatchTimeInterval? = .none, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, @@ -1239,7 +1239,7 @@ public final class RegistryClient: Cancellable { // marked internal for testing func _lookupIdentities( registry: Registry, - scmURL: URL, + scmURL: SourceControlURL, timeout: DispatchTimeInterval?, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, @@ -1672,7 +1672,7 @@ public enum RegistryError: Error, CustomStringConvertible { case failedRetrievingReleaseChecksum(registry: Registry, package: PackageIdentity, version: Version, error: Error) case failedRetrievingManifest(registry: Registry, package: PackageIdentity, version: Version, error: Error) case failedDownloadingSourceArchive(registry: Registry, package: PackageIdentity, version: Version, error: Error) - case failedIdentityLookup(registry: Registry, scmURL: URL, error: Error) + case failedIdentityLookup(registry: Registry, scmURL: SourceControlURL, error: Error) case failedLoadingPackageArchive(AbsolutePath) case failedLoadingPackageMetadata(AbsolutePath) case failedPublishing(Error) @@ -1865,14 +1865,14 @@ extension RegistryClient { public struct PackageMetadata { public let registry: Registry public let versions: [Version] - public let alternateLocations: [URL]? + public let alternateLocations: [SourceControlURL]? } public struct PackageVersionMetadata: Sendable { public let registry: Registry public let licenseURL: URL? public let readmeURL: URL? - public let repositoryURLs: [URL]? + public let repositoryURLs: [SourceControlURL]? public let resources: [Resource] public let author: Author? public let description: String? @@ -1928,7 +1928,7 @@ extension RegistryClient { extension RegistryClient { fileprivate struct AlternativeLocationLink { - let url: URL + let url: SourceControlURL let kind: Kind enum Kind: String { @@ -2079,8 +2079,7 @@ extension HTTPClientHeaders { return nil } - guard let link = fields.first(where: { $0.hasPrefix("<") }).map({ String($0.dropFirst().dropLast()) }), - let url = URL(string: link) + guard let link = fields.first(where: { $0.hasPrefix("<") }).map({ String($0.dropFirst().dropLast()) }) else { return nil } @@ -2092,7 +2091,7 @@ extension HTTPClientHeaders { } return RegistryClient.AlternativeLocationLink( - url: url, + url: SourceControlURL(link), kind: kind ) } diff --git a/Sources/SPMTestSupport/ManifestExtensions.swift b/Sources/SPMTestSupport/ManifestExtensions.swift index bd3315ad3ac..30cbd5bc3a2 100644 --- a/Sources/SPMTestSupport/ManifestExtensions.swift +++ b/Sources/SPMTestSupport/ManifestExtensions.swift @@ -127,7 +127,7 @@ extension Manifest { public static func createRemoteSourceControlManifest( displayName: String, - url: URL, + url: SourceControlURL, path: AbsolutePath, defaultLocalization: String? = nil, platforms: [PlatformDescription] = [], diff --git a/Sources/SPMTestSupport/MockDependency.swift b/Sources/SPMTestSupport/MockDependency.swift index 809deb0abcf..5d519724217 100644 --- a/Sources/SPMTestSupport/MockDependency.swift +++ b/Sources/SPMTestSupport/MockDependency.swift @@ -77,7 +77,8 @@ public struct MockDependency { productFilter: self.products ) - } else if let mappedURL = URL(string: mappedLocation) { + } else { + let mappedURL = SourceControlURL(mappedLocation) let identity = try identityResolver.resolveIdentity(for: mappedURL) return .remoteSourceControl( identity: identity, @@ -86,8 +87,6 @@ public struct MockDependency { requirement: _requirement, productFilter: self.products ) - } else { - throw StringError("invalid mapping of '\(url)' to '\(mappedLocation)'") } case .registry(let identity, let _requirement): let mappedLocation = identityResolver.mappedLocation(for: identity.description) @@ -98,7 +97,8 @@ public struct MockDependency { requirement: _requirement, productFilter: self.products ) - } else if let mappedURL = URL(string: mappedLocation) { + } else { + let mappedURL = SourceControlURL(mappedLocation) let identity = try identityResolver.resolveIdentity(for: mappedURL) let requirement: SourceControlRequirement switch _requirement { @@ -114,8 +114,6 @@ public struct MockDependency { requirement: requirement, productFilter: self.products ) - } else { - throw StringError("invalid mapping of '\(identity)' to '\(mappedLocation)'") } } @@ -138,10 +136,10 @@ public struct MockDependency { } public static func sourceControl(url: String, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { - .sourceControl(url: URL(string: url)!, requirement: requirement, products: products) + .sourceControl(url: SourceControlURL(url), requirement: requirement, products: products) } - public static func sourceControl(url: URL, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { + public static func sourceControl(url: SourceControlURL, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { MockDependency(location: .remoteSourceControl(url: url, requirement: requirement), products: products) } @@ -156,7 +154,7 @@ public struct MockDependency { public enum Location { case fileSystem(path: RelativePath) case localSourceControl(path: RelativePath, requirement: SourceControlRequirement) - case remoteSourceControl(url: URL, requirement: SourceControlRequirement) + case remoteSourceControl(url: SourceControlURL, requirement: SourceControlRequirement) case registry(identity: PackageIdentity, requirement: RegistryRequirement) } } diff --git a/Sources/SPMTestSupport/MockPackage.swift b/Sources/SPMTestSupport/MockPackage.swift index 87919a4c688..d692b883930 100644 --- a/Sources/SPMTestSupport/MockPackage.swift +++ b/Sources/SPMTestSupport/MockPackage.swift @@ -63,7 +63,7 @@ public struct MockPackage { ) { self.name = name self.platforms = platforms - self.location = .sourceControl(url: URL(string: url)!) + self.location = .sourceControl(url: SourceControlURL(url)) self.targets = targets self.products = products self.dependencies = dependencies @@ -115,7 +115,7 @@ public struct MockPackage { public enum Location { case fileSystem(path: RelativePath) - case sourceControl(url: URL) + case sourceControl(url: SourceControlURL) case registry(identity: PackageIdentity, alternativeURLs: [URL]?, metadata: RegistryReleaseMetadata?) } } diff --git a/Sources/SPMTestSupport/MockWorkspace.swift b/Sources/SPMTestSupport/MockWorkspace.swift index cb3d67d7a44..3407a0d9a3d 100644 --- a/Sources/SPMTestSupport/MockWorkspace.swift +++ b/Sources/SPMTestSupport/MockWorkspace.swift @@ -795,7 +795,7 @@ public final class MockWorkspace { return } - XCTAssertEqual(pin.packageRef.kind, .remoteSourceControl(URL(string: url)!), file: file, line: line) + XCTAssertEqual(pin.packageRef.kind, .remoteSourceControl(SourceControlURL(url)), file: file, line: line) } } diff --git a/Sources/SPMTestSupport/PackageDependencyDescriptionExtensions.swift b/Sources/SPMTestSupport/PackageDependencyDescriptionExtensions.swift index 04233baf846..3a167448a6f 100644 --- a/Sources/SPMTestSupport/PackageDependencyDescriptionExtensions.swift +++ b/Sources/SPMTestSupport/PackageDependencyDescriptionExtensions.swift @@ -45,7 +45,7 @@ public extension PackageDependency { static func remoteSourceControl(identity: PackageIdentity? = nil, deprecatedName: String? = nil, - url: URL, + url: SourceControlURL, requirement: SourceControl.Requirement, productFilter: ProductFilter = .everything ) -> Self { diff --git a/Sources/SourceControl/GitRepository.swift b/Sources/SourceControl/GitRepository.swift index 6fb5e0037d1..a7a47c72e32 100644 --- a/Sources/SourceControl/GitRepository.swift +++ b/Sources/SourceControl/GitRepository.swift @@ -192,7 +192,7 @@ public struct GitRepositoryProvider: RepositoryProvider, Cancellable { try self.clone( repository, - repository.location.gitURL, + repository.location.SourceControlURL, path.pathString, ["--mirror"], progress: progressHandler @@ -256,7 +256,7 @@ public struct GitRepositoryProvider: RepositoryProvider, Cancellable { // In destination repo remove the remote which will be pointing to the source repo. let clone = GitRepository(git: self.git, path: destinationPath) // Set the original remote to the new clone. - try clone.setURL(remote: origin, url: repository.location.gitURL) + try clone.setURL(remote: origin, url: repository.location.SourceControlURL) // FIXME: This is unfortunate that we have to fetch to update remote's data. try clone.fetch() } else { @@ -1382,7 +1382,7 @@ private func gitFetchStatusFilter(_ bytes: [UInt8], progress: FetchProgress.Hand } extension RepositorySpecifier.Location { - fileprivate var gitURL: String { + fileprivate var SourceControlURL: String { switch self { case .path(let path): return path.pathString diff --git a/Sources/SourceControl/Repository.swift b/Sources/SourceControl/Repository.swift index e41a7f1635a..9fdf6a9309c 100644 --- a/Sources/SourceControl/Repository.swift +++ b/Sources/SourceControl/Repository.swift @@ -27,30 +27,35 @@ public struct RepositorySpecifier: Hashable, Sendable { } /// Create a specifier on a URL. - public init(url: URL) { + public init(url: SourceControlURL) { self.init(location: .url(url)) } - /// The location of the repository as URL. - public var url: URL { + /// The location of the repository as string. + public var url: String { switch self.location { - case .path(let path): return URL(fileURLWithPath: path.pathString) - case .url(let url): return url + case .path(let path): return path.pathString + case .url(let url): return url.absoluteString } } /// Returns the cleaned basename for the specifier. public var basename: String { - var basename = self.url.pathComponents.dropFirst(1).last(where: { !$0.isEmpty }) ?? "" + // FIXME: this might be wrong + //var basename = self.url.pathComponents.dropFirst(1).last(where: { !$0.isEmpty }) ?? "" + var basename = (self.url as NSString).lastPathComponent if basename.hasSuffix(".git") { basename = String(basename.dropLast(4)) } + if basename == "/" { + return "" + } return basename } public enum Location: Hashable, CustomStringConvertible, Sendable { case path(AbsolutePath) - case url(URL) + case url(SourceControlURL) public var description: String { switch self { diff --git a/Sources/Workspace/Workspace+State.swift b/Sources/Workspace/Workspace+State.swift index 61cd4887044..f5e152e34ed 100644 --- a/Sources/Workspace/Workspace+State.swift +++ b/Sources/Workspace/Workspace+State.swift @@ -480,10 +480,7 @@ extension PackageModel.PackageReference { case .localSourceControl: kind = try .localSourceControl(.init(validating: reference.location)) case .remoteSourceControl: - guard let url = URL(string: reference.location) else { - throw StringError("invalid url \(reference.location)") - } - kind = .remoteSourceControl(url) + kind = .remoteSourceControl(SourceControlURL(reference.location)) case .registry: kind = .registry(identity) } @@ -800,10 +797,7 @@ extension PackageModel.PackageReference { case .localSourceControl: kind = try .localSourceControl(.init(validating: reference.location)) case .remoteSourceControl: - guard let url = URL(string: reference.location) else { - throw StringError("invalid url \(reference.location)") - } - kind = .remoteSourceControl(url) + kind = .remoteSourceControl(SourceControlURL(reference.location)) case .registry: kind = .registry(identity) } @@ -1040,10 +1034,8 @@ extension PackageModel.PackageReference { case "remote": if let path = try? AbsolutePath(validating: reference.location) { kind = .localSourceControl(path) - } else if let url = URL(string: reference.location) { - kind = .remoteSourceControl(url) } else { - throw StringError("invalid package kind \(reference.kind)") + kind = .remoteSourceControl(SourceControlURL(reference.location)) } default: throw StringError("invalid package kind \(reference.kind)") diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 93a8d4c58f9..48854b1f9c4 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -3953,7 +3953,7 @@ extension Workspace { private let transformationMode: TransformationMode private let cacheTTL = DispatchTimeInterval.seconds(300) // 5m - private let identityLookupCache = ThreadSafeKeyValueStore, expirationTime: DispatchTime)>() + private let identityLookupCache = ThreadSafeKeyValueStore, expirationTime: DispatchTime)>() init(underlying: ManifestLoaderProtocol, registryClient: RegistryClient, transformationMode: TransformationMode) { self.underlying = underlying @@ -4183,7 +4183,7 @@ extension Workspace { } private func mapRegistryIdentity( - url: URL, + url: SourceControlURL, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, completion: @escaping (Result) -> Void diff --git a/Tests/PackageCollectionsTests/GitHubPackageMetadataProviderTests.swift b/Tests/PackageCollectionsTests/GitHubPackageMetadataProviderTests.swift index 7269b73c678..cfccc53de67 100644 --- a/Tests/PackageCollectionsTests/GitHubPackageMetadataProviderTests.swift +++ b/Tests/PackageCollectionsTests/GitHubPackageMetadataProviderTests.swift @@ -47,7 +47,7 @@ class GitHubPackageMetadataProviderTests: XCTestCase { func testGood() throws { try testWithTemporaryDirectory { tmpPath in - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") let apiURL = URL("https://api.github.com/repos/octocat/Hello-World") let releasesURL = URL("https://api.github.com/repos/octocat/Hello-World/releases?per_page=20") @@ -129,7 +129,7 @@ class GitHubPackageMetadataProviderTests: XCTestCase { func testRepoNotFound() throws { try testWithTemporaryDirectory { tmpPath in - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") let handler: LegacyHTTPClient.Handler = { _, _, completion in completion(.success(.init(statusCode: 404))) @@ -151,7 +151,7 @@ class GitHubPackageMetadataProviderTests: XCTestCase { func testOthersNotFound() throws { try testWithTemporaryDirectory { tmpPath in - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") let apiURL = URL("https://api.github.com/repos/octocat/Hello-World") try fixture(name: "Collections", createGitRepo: false) { fixturePath in @@ -189,7 +189,7 @@ class GitHubPackageMetadataProviderTests: XCTestCase { func testPermissionDenied() throws { try testWithTemporaryDirectory { tmpPath in - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") let apiURL = URL("https://api.github.com/repos/octocat/Hello-World") let handler: LegacyHTTPClient.Handler = { _, _, completion in @@ -212,7 +212,7 @@ class GitHubPackageMetadataProviderTests: XCTestCase { func testInvalidAuthToken() throws { try testWithTemporaryDirectory { tmpPath in - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") let apiURL = URL("https://api.github.com/repos/octocat/Hello-World") let authTokens = [AuthTokenType.github("github.com"): "foo"] @@ -242,7 +242,7 @@ class GitHubPackageMetadataProviderTests: XCTestCase { func testAPILimit() throws { try testWithTemporaryDirectory { tmpPath in - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") let apiURL = URL("https://api.github.com/repos/octocat/Hello-World") let total = 5 @@ -302,7 +302,7 @@ class GitHubPackageMetadataProviderTests: XCTestCase { let url = UUID().uuidString let identity = PackageIdentity(urlString: url) XCTAssertThrowsError(try provider.syncGet(identity: identity, location: url), "should throw error") { error in - XCTAssertEqual(error as? GitHubPackageMetadataProviderError, .invalidGitURL(url)) + XCTAssertEqual(error as? GitHubPackageMetadataProviderError, .invalidSourceControlURL(url)) } } } @@ -319,7 +319,7 @@ class GitHubPackageMetadataProviderTests: XCTestCase { let path = AbsolutePath.root let identity = PackageIdentity(path: path) XCTAssertThrowsError(try provider.syncGet(identity: identity, location: path.pathString), "should throw error") { error in - XCTAssertEqual(error as? GitHubPackageMetadataProviderError, .invalidGitURL(path.pathString)) + XCTAssertEqual(error as? GitHubPackageMetadataProviderError, .invalidSourceControlURL(path.pathString)) } } } @@ -331,7 +331,7 @@ class GitHubPackageMetadataProviderTests: XCTestCase { try XCTSkipIf(true) #endif - let repoURL = URL("https://github.com/apple/swift-numerics.git") + let repoURL = SourceControlURL("https://github.com/apple/swift-numerics.git") let httpClient = LegacyHTTPClient() httpClient.configuration.circuitBreakerStrategy = .none diff --git a/Tests/PackageCollectionsTests/PackageIndexTests.swift b/Tests/PackageCollectionsTests/PackageIndexTests.swift index 87fb27a90e8..fdaceb5d271 100644 --- a/Tests/PackageCollectionsTests/PackageIndexTests.swift +++ b/Tests/PackageCollectionsTests/PackageIndexTests.swift @@ -23,7 +23,7 @@ class PackageIndexTests: XCTestCase { var configuration = PackageIndexConfiguration(url: url, disableCache: true) configuration.enabled = true - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") let packageIdentity = PackageIdentity(url: repoURL) let package = makeMockPackage(id: "test-package") let handler: LegacyHTTPClient.Handler = { request, _, completion in @@ -59,7 +59,7 @@ class PackageIndexTests: XCTestCase { let index = PackageIndex(configuration: configuration, callbackQueue: .sharedConcurrent, observabilityScope: ObservabilitySystem.NOOP) defer { XCTAssertNoThrow(try index.close()) } - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") XCTAssertThrowsError(try temp_await { callback in index.getPackageMetadata(identity: .init(url: repoURL), location: repoURL.absoluteString, callback: callback) }) { error in XCTAssertEqual(error as? PackageIndexError, .featureDisabled) } @@ -72,7 +72,7 @@ class PackageIndexTests: XCTestCase { let index = PackageIndex(configuration: configuration, callbackQueue: .sharedConcurrent, observabilityScope: ObservabilitySystem.NOOP) defer { XCTAssertNoThrow(try index.close()) } - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") XCTAssertThrowsError(try temp_await { callback in index.getPackageMetadata(identity: .init(url: repoURL), location: repoURL.absoluteString, callback: callback) }) { error in XCTAssertEqual(error as? PackageIndexError, .notConfigured) } @@ -246,7 +246,7 @@ class PackageIndexTests: XCTestCase { var configuration = PackageIndexConfiguration(url: url, disableCache: true) configuration.enabled = true - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") let packageIdentity = PackageIdentity(url: repoURL) let package = makeMockPackage(id: "test-package") let handler: LegacyHTTPClient.Handler = { request, _, completion in @@ -280,7 +280,7 @@ class PackageIndexTests: XCTestCase { let index = PackageIndex(configuration: configuration, callbackQueue: .sharedConcurrent, observabilityScope: ObservabilitySystem.NOOP) defer { XCTAssertNoThrow(try index.close()) } - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") XCTAssertThrowsError(try index.syncGet(identity: .init(url: repoURL), location: repoURL.absoluteString)) { error in XCTAssertEqual(error as? PackageIndexError, .featureDisabled) } @@ -293,7 +293,7 @@ class PackageIndexTests: XCTestCase { let index = PackageIndex(configuration: configuration, callbackQueue: .sharedConcurrent, observabilityScope: ObservabilitySystem.NOOP) defer { XCTAssertNoThrow(try index.close()) } - let repoURL = URL("https://github.com/octocat/Hello-World.git") + let repoURL = SourceControlURL("https://github.com/octocat/Hello-World.git") XCTAssertThrowsError(try index.syncGet(identity: .init(url: repoURL), location: repoURL.absoluteString)) { error in XCTAssertEqual(error as? PackageIndexError, .notConfigured) } diff --git a/Tests/PackageFingerprintTests/FilePackageFingerprintStorageTests.swift b/Tests/PackageFingerprintTests/FilePackageFingerprintStorageTests.swift index eb665f16977..549693552ee 100644 --- a/Tests/PackageFingerprintTests/FilePackageFingerprintStorageTests.swift +++ b/Tests/PackageFingerprintTests/FilePackageFingerprintStorageTests.swift @@ -27,7 +27,7 @@ final class FilePackageFingerprintStorageTests: XCTestCase { let directoryPath = AbsolutePath("/fingerprints") let storage = FilePackageFingerprintStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) let registryURL = URL("https://example.packages.com") - let sourceControlURL = URL("https://example.com/mona/LinkedList.git") + let sourceControlURL = SourceControlURL("https://example.com/mona/LinkedList.git") // Add fingerprints for mona.LinkedList let package = PackageIdentity.plain("mona.LinkedList") @@ -73,7 +73,7 @@ final class FilePackageFingerprintStorageTests: XCTestCase { let registryFingerprints = fingerprints[.registry] XCTAssertEqual(registryFingerprints?.count, 1) - XCTAssertEqual(registryFingerprints?[.sourceCode]?.origin.url, registryURL) + XCTAssertEqual(registryFingerprints?[.sourceCode]?.origin.url, SourceControlURL(registryURL)) XCTAssertEqual(registryFingerprints?[.sourceCode]?.value, "checksum-1.0.0") let scmFingerprints = fingerprints[.sourceControl] @@ -88,7 +88,7 @@ final class FilePackageFingerprintStorageTests: XCTestCase { let registryFingerprints = fingerprints[.registry] XCTAssertEqual(registryFingerprints?.count, 1) - XCTAssertEqual(registryFingerprints?[.sourceCode]?.origin.url, registryURL) + XCTAssertEqual(registryFingerprints?[.sourceCode]?.origin.url, SourceControlURL(registryURL)) XCTAssertEqual(registryFingerprints?[.sourceCode]?.value, "checksum-1.1.0") } @@ -98,7 +98,7 @@ final class FilePackageFingerprintStorageTests: XCTestCase { let registryFingerprints = fingerprints[.registry] XCTAssertEqual(registryFingerprints?.count, 1) - XCTAssertEqual(registryFingerprints?[.sourceCode]?.origin.url, registryURL) + XCTAssertEqual(registryFingerprints?[.sourceCode]?.origin.url, SourceControlURL(registryURL)) XCTAssertEqual(registryFingerprints?[.sourceCode]?.value, "checksum-1.0.0") } } @@ -189,7 +189,7 @@ final class FilePackageFingerprintStorageTests: XCTestCase { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/fingerprints") let storage = FilePackageFingerprintStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) - let sourceControlURL = URL("https://example.com/mona/LinkedList.git") + let sourceControlURL = SourceControlURL("https://example.com/mona/LinkedList.git") let packageRef = PackageReference.remoteSourceControl( identity: PackageIdentity(url: sourceControlURL), url: sourceControlURL @@ -229,8 +229,8 @@ final class FilePackageFingerprintStorageTests: XCTestCase { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/fingerprints") let storage = FilePackageFingerprintStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) - let fooURL = URL("https://example.com/foo/LinkedList.git") - let barURL = URL("https://example.com/bar/LinkedList.git") + let fooURL = SourceControlURL("https://example.com/foo/LinkedList.git") + let barURL = SourceControlURL("https://example.com/bar/LinkedList.git") // foo and bar have the same identity `LinkedList` let fooRef = PackageReference.remoteSourceControl(identity: PackageIdentity(url: fooURL), url: fooURL) @@ -289,7 +289,7 @@ final class FilePackageFingerprintStorageTests: XCTestCase { try mockFileSystem.createDirectory(directoryPath, recursive: true) let storage = FilePackageFingerprintStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) - let sourceControlURL = URL("https://example.com/mona/LinkedList.git") + let sourceControlURL = SourceControlURL("https://example.com/mona/LinkedList.git") let package = PackageIdentity.plain("mona.LinkedList") let fingerprintsPath = directoryPath.appending(package.fingerprintsFilename()) let v1Fingerprints = """ @@ -329,7 +329,7 @@ final class FilePackageFingerprintStorageTests: XCTestCase { let directoryPath = AbsolutePath("/fingerprints") let storage = FilePackageFingerprintStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) let registryURL = URL("https://example.packages.com") - let sourceControlURL = URL("https://example.com/mona/LinkedList.git") + let sourceControlURL = SourceControlURL("https://example.com/mona/LinkedList.git") // Add fingerprints for 1.0.0 source archive/code let package = PackageIdentity.plain("mona.LinkedList") @@ -388,11 +388,11 @@ final class FilePackageFingerprintStorageTests: XCTestCase { let registryFingerprints = fingerprints[.registry] XCTAssertEqual(registryFingerprints?.count, 3) - XCTAssertEqual(registryFingerprints?[.sourceCode]?.origin.url, registryURL) + XCTAssertEqual(registryFingerprints?[.sourceCode]?.origin.url, SourceControlURL(registryURL)) XCTAssertEqual(registryFingerprints?[.sourceCode]?.value, "archive-checksum-1.0.0") - XCTAssertEqual(registryFingerprints?[.manifest(.none)]?.origin.url, registryURL) + XCTAssertEqual(registryFingerprints?[.manifest(.none)]?.origin.url, SourceControlURL(registryURL)) XCTAssertEqual(registryFingerprints?[.manifest(.none)]?.value, "manifest-checksum-1.0.0") - XCTAssertEqual(registryFingerprints?[.manifest(ToolsVersion.v5_6)]?.origin.url, registryURL) + XCTAssertEqual(registryFingerprints?[.manifest(ToolsVersion.v5_6)]?.origin.url, SourceControlURL(registryURL)) XCTAssertEqual(registryFingerprints?[.manifest(ToolsVersion.v5_6)]?.value, "manifest-5.6-checksum-1.0.0") let scmFingerprints = fingerprints[.sourceControl] diff --git a/Tests/PackageGraphTests/PubgrubTests.swift b/Tests/PackageGraphTests/PubgrubTests.swift index b4c0ed4eb8a..35a53f05060 100644 --- a/Tests/PackageGraphTests/PubgrubTests.swift +++ b/Tests/PackageGraphTests/PubgrubTests.swift @@ -306,7 +306,7 @@ final class PubgrubTests: XCTestCase { } func testUpdatePackageIdentifierAfterResolution() throws { - let fooURL = URL("https://example.com/foo") + let fooURL = SourceControlURL("https://example.com/foo") let fooRef = PackageReference.remoteSourceControl(identity: PackageIdentity(url: fooURL), url: fooURL) let foo = MockContainer(package: fooRef, dependenciesByVersion: [v1: [:]]) foo.manifestName = "bar" diff --git a/Tests/PackageLoadingTests/PD_5_6_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_6_LoadingTests.swift index 4b61a28dc9e..9efa3481184 100644 --- a/Tests/PackageLoadingTests/PD_5_6_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_6_LoadingTests.swift @@ -230,7 +230,8 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { let observability = ObservabilitySystem.makeForTesting() let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) - XCTAssertNoDiagnostics(observability.diagnostics) + // FIXME: temporary filter a diagnostic that shows up on macOS 14.0 + XCTAssertNoDiagnostics(observability.diagnostics.filter { !$0.message.contains("coreservicesd") }) XCTAssertNoDiagnostics(validationDiagnostics) let files = manifest.displayName.split(separator: ",").map(String.init) diff --git a/Tests/PackageLoadingTests/TargetSourcesBuilderTests.swift b/Tests/PackageLoadingTests/TargetSourcesBuilderTests.swift index 8b88b05d5e2..29a5f3fbbdf 100644 --- a/Tests/PackageLoadingTests/TargetSourcesBuilderTests.swift +++ b/Tests/PackageLoadingTests/TargetSourcesBuilderTests.swift @@ -718,7 +718,7 @@ class TargetSourcesBuilderTests: XCTestCase { let builder = TargetSourcesBuilder( packageIdentity: .plain("test"), - packageKind: .remoteSourceControl(URL("https://some.where/foo/bar")), + packageKind: .remoteSourceControl(SourceControlURL("https://some.where/foo/bar")), packagePath: "/test", target: target, path: .root, @@ -785,7 +785,7 @@ class TargetSourcesBuilderTests: XCTestCase { let builder = TargetSourcesBuilder( packageIdentity: .plain("test"), - packageKind: .remoteSourceControl(URL("https://some.where/foo/bar")), + packageKind: .remoteSourceControl(SourceControlURL("https://some.where/foo/bar")), packagePath: .root, target: target, path: .root, @@ -932,7 +932,7 @@ class TargetSourcesBuilderTests: XCTestCase { let builder = TargetSourcesBuilder( packageIdentity: .plain("test"), - packageKind: .remoteSourceControl(URL("https://some.where/foo/bar")), + packageKind: .remoteSourceControl(SourceControlURL("https://some.where/foo/bar")), packagePath: .root, target: target, path: .root, diff --git a/Tests/PackageRegistryTests/PackageVersionChecksumTOFUTests.swift b/Tests/PackageRegistryTests/PackageVersionChecksumTOFUTests.swift index 3dd1b87ae84..faad39ead06 100644 --- a/Tests/PackageRegistryTests/PackageVersionChecksumTOFUTests.swift +++ b/Tests/PackageRegistryTests/PackageVersionChecksumTOFUTests.swift @@ -114,7 +114,7 @@ final class PackageVersionChecksumTOFUTests: XCTestCase { callback: callback ) } - XCTAssertEqual(registryURL, fingerprint.origin.url) + XCTAssertEqual(SourceControlURL(registryURL), fingerprint.origin.url) XCTAssertEqual(checksum, fingerprint.value) } @@ -710,7 +710,7 @@ final class PackageVersionChecksumTOFUTests: XCTestCase { callback: callback ) } - XCTAssertEqual(registryURL, fingerprint.origin.url) + XCTAssertEqual(SourceControlURL(registryURL), fingerprint.origin.url) XCTAssertEqual("Package.swift checksum", fingerprint.value) } do { @@ -725,7 +725,7 @@ final class PackageVersionChecksumTOFUTests: XCTestCase { callback: callback ) } - XCTAssertEqual(registryURL, fingerprint.origin.url) + XCTAssertEqual(SourceControlURL(registryURL), fingerprint.origin.url) XCTAssertEqual("Package@swift-5.6.swift checksum", fingerprint.value) } } diff --git a/Tests/PackageRegistryTests/RegistryClientTests.swift b/Tests/PackageRegistryTests/RegistryClientTests.swift index 221984c5ce0..8cff2d77f66 100644 --- a/Tests/PackageRegistryTests/RegistryClientTests.swift +++ b/Tests/PackageRegistryTests/RegistryClientTests.swift @@ -90,10 +90,10 @@ final class RegistryClientTests: XCTestCase { let metadata = try registryClient.getPackageMetadata(package: identity) XCTAssertEqual(metadata.versions, ["1.1.1", "1.0.0"]) XCTAssertEqual(metadata.alternateLocations!, [ - URL("https://github.com/mona/LinkedList"), - URL("ssh://git@github.com:mona/LinkedList.git"), - URL("git@github.com:mona/LinkedList.git"), - URL("https://gitlab.com/mona/LinkedList"), + SourceControlURL("https://github.com/mona/LinkedList"), + SourceControlURL("ssh://git@github.com:mona/LinkedList.git"), + SourceControlURL("git@github.com:mona/LinkedList.git"), + SourceControlURL("https://gitlab.com/mona/LinkedList"), ]) } @@ -258,9 +258,9 @@ final class RegistryClientTests: XCTestCase { XCTAssertEqual(metadata.licenseURL, URL("https://github.com/mona/LinkedList/license")) XCTAssertEqual(metadata.readmeURL, URL("https://github.com/mona/LinkedList/readme")) XCTAssertEqual(metadata.repositoryURLs!, [ - URL("https://github.com/mona/LinkedList"), - URL("ssh://git@github.com:mona/LinkedList.git"), - URL("git@github.com:mona/LinkedList.git"), + SourceControlURL("https://github.com/mona/LinkedList"), + SourceControlURL("ssh://git@github.com:mona/LinkedList.git"), + SourceControlURL("git@github.com:mona/LinkedList.git"), ]) } @@ -1895,9 +1895,9 @@ final class RegistryClientTests: XCTestCase { let licenseURL = URL("https://github.com/\(identity.scope)/\(identity.name)/license") let readmeURL = URL("https://github.com/\(identity.scope)/\(identity.name)/readme") let repositoryURLs = [ - URL("https://github.com/\(identity.scope)/\(identity.name)"), - URL("ssh://git@github.com:\(identity.scope)/\(identity.name).git"), - URL("git@github.com:\(identity.scope)/\(identity.name).git"), + SourceControlURL("https://github.com/\(identity.scope)/\(identity.name)"), + SourceControlURL("ssh://git@github.com:\(identity.scope)/\(identity.name).git"), + SourceControlURL("git@github.com:\(identity.scope)/\(identity.name).git"), ] let handler: LegacyHTTPClient.Handler = { request, _, completion in @@ -2550,7 +2550,7 @@ final class RegistryClientTests: XCTestCase { callback: callback ) } - XCTAssertEqual(registryURL, fingerprint.origin.url) + XCTAssertEqual(SourceControlURL(registryURL), fingerprint.origin.url) XCTAssertEqual(checksum, fingerprint.value) } @@ -2877,7 +2877,7 @@ final class RegistryClientTests: XCTestCase { func testLookupIdentities() throws { let registryURL = URL("https://packages.example.com") - let packageURL = URL("https://example.com/mona/LinkedList") + let packageURL = SourceControlURL("https://example.com/mona/LinkedList") let identifiersURL = URL("\(registryURL)/identifiers?url=\(packageURL.absoluteString)") let handler: LegacyHTTPClient.Handler = { request, _, completion in @@ -2921,7 +2921,7 @@ final class RegistryClientTests: XCTestCase { func testLookupIdentities404() throws { let registryURL = URL("https://packages.example.com") - let packageURL = URL("https://example.com/mona/LinkedList") + let packageURL = SourceControlURL("https://example.com/mona/LinkedList") let identifiersURL = URL("\(registryURL)/identifiers?url=\(packageURL.absoluteString)") let handler: LegacyHTTPClient.Handler = { request, _, completion in @@ -2948,7 +2948,7 @@ final class RegistryClientTests: XCTestCase { func testLookupIdentities_ServerError() throws { let registryURL = URL("https://packages.example.com") - let packageURL = URL("https://example.com/mona/LinkedList") + let packageURL = SourceControlURL("https://example.com/mona/LinkedList") let identifiersURL = URL("\(registryURL)/identifiers?url=\(packageURL.absoluteString)") let serverErrorHandler = ServerErrorHandler( @@ -2982,7 +2982,7 @@ final class RegistryClientTests: XCTestCase { func testRequestAuthorization_token() throws { let registryURL = URL("https://packages.example.com") - let packageURL = URL("https://example.com/mona/LinkedList") + let packageURL = SourceControlURL("https://example.com/mona/LinkedList") let identifiersURL = URL("\(registryURL)/identifiers?url=\(packageURL.absoluteString)") let token = "top-sekret" @@ -3036,7 +3036,7 @@ final class RegistryClientTests: XCTestCase { func testRequestAuthorization_basic() throws { let registryURL = URL("https://packages.example.com") - let packageURL = URL("https://example.com/mona/LinkedList") + let packageURL = SourceControlURL("https://example.com/mona/LinkedList") let identifiersURL = URL("\(registryURL)/identifiers?url=\(packageURL.absoluteString)") let user = "jappleseed" @@ -3912,7 +3912,7 @@ extension RegistryClient { } } - fileprivate func lookupIdentities(scmURL: URL) throws -> Set { + fileprivate func lookupIdentities(scmURL: SourceControlURL) throws -> Set { try temp_await { self.lookupIdentities( scmURL: scmURL, diff --git a/Tests/SourceControlTests/RepositoryManagerTests.swift b/Tests/SourceControlTests/RepositoryManagerTests.swift index 84881b60215..854c67bf3f6 100644 --- a/Tests/SourceControlTests/RepositoryManagerTests.swift +++ b/Tests/SourceControlTests/RepositoryManagerTests.swift @@ -354,7 +354,7 @@ class RepositoryManagerTests: XCTestCase { group.enter() delegate.prepare(fetchExpected: index == 0, updateExpected: index > 0) manager.lookup( - package: .init(url: dummyRepo.url), + package: .init(url: SourceControlURL(dummyRepo.url)), repository: dummyRepo, updateStrategy: .always, observabilityScope: observability.topScope, @@ -459,7 +459,7 @@ class RepositoryManagerTests: XCTestCase { provider.startGroup.enter() finishGroup.enter() manager.lookup( - package: .init(url: repository.url), + package: .init(urlString: repository.url), repository: repository, updateStrategy: .never, observabilityScope: observability.topScope, @@ -598,7 +598,7 @@ extension RepositoryManager { ) throws -> RepositoryHandle { return try temp_await { self.lookup( - package: .init(url: repository.url), + package: .init(url: SourceControlURL(repository.url)), repository: repository, updateStrategy: updateStrategy, observabilityScope: observabilityScope, @@ -679,7 +679,7 @@ private class DummyRepositoryProvider: RepositoryProvider { } // We only support one dummy URL. - let basename = repository.url.pathComponents.last! + let basename = (repository.url as NSString).lastPathComponent if basename != "dummy" { throw DummyError.invalidRepository } diff --git a/Tests/WorkspaceTests/PinsStoreTests.swift b/Tests/WorkspaceTests/PinsStoreTests.swift index d0c3e8a9972..f55ca3142f8 100644 --- a/Tests/WorkspaceTests/PinsStoreTests.swift +++ b/Tests/WorkspaceTests/PinsStoreTests.swift @@ -288,16 +288,16 @@ final class PinsStoreTests: XCTestCase { } func testPinsWithMirrors() throws { - let fooURL = URL("https://github.com/corporate/foo.git") + let fooURL = SourceControlURL("https://github.com/corporate/foo.git") let fooIdentity = PackageIdentity(url: fooURL) - let fooMirroredURL = URL("https://github.corporate.com/team/foo.git") + let fooMirroredURL = SourceControlURL("https://github.corporate.com/team/foo.git") - let barURL = URL("https://github.com/corporate/baraka.git") + let barURL = SourceControlURL("https://github.com/corporate/baraka.git") let barIdentity = PackageIdentity(url: barURL) - let barMirroredURL = URL("https://github.corporate.com/team/bar.git") + let barMirroredURL = SourceControlURL("https://github.corporate.com/team/bar.git") let barMirroredIdentity = PackageIdentity(url: barMirroredURL) - let bazURL = URL("https://github.com/cool/baz.git") + let bazURL = SourceControlURL("https://github.com/cool/baz.git") let bazIdentity = PackageIdentity(url: bazURL) let mirrors = DependencyMirrors() @@ -344,11 +344,11 @@ final class PinsStoreTests: XCTestCase { func testPinsWithMirrorsDeterminism() throws { let fooIdentity = PackageIdentity.plain("foo") - let fooURL1 = URL("https://github.com/corporate/foo") - let fooURL2 = URL("https://github.com/corporate/foo.git") - let fooURL3 = URL("https://github.com/old-corporate/foo") - let fooURL4 = URL("https://github.com/old-corporate/foo.git") - let fooMirroredURL = URL("https://github.corporate.com/team/foo") + let fooURL1 = SourceControlURL("https://github.com/corporate/foo") + let fooURL2 = SourceControlURL("https://github.com/corporate/foo.git") + let fooURL3 = SourceControlURL("https://github.com/old-corporate/foo") + let fooURL4 = SourceControlURL("https://github.com/old-corporate/foo.git") + let fooMirroredURL = SourceControlURL("https://github.corporate.com/team/foo") let mirrors = DependencyMirrors() mirrors.set(mirror: fooMirroredURL.absoluteString, for: fooURL1.absoluteString) diff --git a/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift b/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift index 12e63044e98..411f04c3645 100644 --- a/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift +++ b/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift @@ -43,7 +43,7 @@ private class MockRepository: Repository { self.versions = versions } - init(fs: FileSystem, url: URL, versions: [Version: Manifest]) { + init(fs: FileSystem, url: SourceControlURL, versions: [Version: Manifest]) { self.fs = fs self.location = .url(url) self.packageRef = .remoteSourceControl(identity: PackageIdentity(url: url), url: url) diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index 41c5f203256..6c06ff223ca 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -13032,7 +13032,7 @@ final class WorkspaceTests: XCTestCase { try customFS.createDirectory(targetDir, recursive: true) try customFS.writeFileContents(targetDir.appending("file.swift"), bytes: "") - let bazURL = URL("https://example.com/baz") + let bazURL = SourceControlURL("https://example.com/baz") let bazPackageReference = PackageReference( identity: PackageIdentity(url: bazURL), kind: .remoteSourceControl(bazURL)