From 6632a918175a66efe969e3b50bae2ba0517d58c0 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 16 May 2025 09:55:34 -0400 Subject: [PATCH] Convert git WorkingCopy APIs to async `RepositoryProviders` may not always create and open working copies synchronously. This avoids the need for concurrency workarounds that are prone to deadlocks. rdar://149982696 --- Sources/Commands/Utilities/APIDigester.swift | 2 +- Sources/SourceControl/GitRepository.swift | 2 +- Sources/SourceControl/Repository.swift | 4 +- Sources/SourceControl/RepositoryManager.swift | 14 +-- Sources/Workspace/Workspace+Editing.swift | 4 +- .../Workspace/Workspace+SourceControl.swift | 10 +-- Sources/Workspace/Workspace.swift | 2 +- .../InMemoryGitRepository.swift | 6 +- .../GitRepositoryTests.swift | 88 +++++++++---------- .../InMemoryGitRepositoryTests.swift | 6 +- .../RepositoryManagerTests.swift | 18 ++-- Tests/WorkspaceTests/WorkspaceTests.swift | 2 +- 12 files changed, 79 insertions(+), 79 deletions(-) diff --git a/Sources/Commands/Utilities/APIDigester.swift b/Sources/Commands/Utilities/APIDigester.swift index d989c8abd76..208d78adbea 100644 --- a/Sources/Commands/Utilities/APIDigester.swift +++ b/Sources/Commands/Utilities/APIDigester.swift @@ -104,7 +104,7 @@ struct APIDigesterBaselineDumper { // Clone the current package in a sandbox and checkout the baseline revision. let repositoryProvider = GitRepositoryProvider() let specifier = RepositorySpecifier(path: baselinePackageRoot) - let workingCopy = try repositoryProvider.createWorkingCopy( + let workingCopy = try await repositoryProvider.createWorkingCopy( repository: specifier, sourcePath: packageRoot, at: baselinePackageRoot, diff --git a/Sources/SourceControl/GitRepository.swift b/Sources/SourceControl/GitRepository.swift index 2b6bebb9955..fab1e9ab28f 100644 --- a/Sources/SourceControl/GitRepository.swift +++ b/Sources/SourceControl/GitRepository.swift @@ -229,7 +229,7 @@ public struct GitRepositoryProvider: RepositoryProvider, Cancellable { sourcePath: Basics.AbsolutePath, at destinationPath: Basics.AbsolutePath, editable: Bool - ) throws -> WorkingCheckout { + ) async throws -> WorkingCheckout { if editable { // For editable clones, i.e. the user is expected to directly work on them, first we create // a clone from our cache of repositories and then we replace the remote to the one originally diff --git a/Sources/SourceControl/Repository.swift b/Sources/SourceControl/Repository.swift index a86227b7271..22c7895811f 100644 --- a/Sources/SourceControl/Repository.swift +++ b/Sources/SourceControl/Repository.swift @@ -121,7 +121,7 @@ public protocol RepositoryProvider: Cancellable { repository: RepositorySpecifier, sourcePath: AbsolutePath, at destinationPath: AbsolutePath, - editable: Bool) throws -> WorkingCheckout + editable: Bool) async throws -> WorkingCheckout /// Returns true if a working repository exists at `path` func workingCopyExists(at path: AbsolutePath) throws -> Bool @@ -131,7 +131,7 @@ public protocol RepositoryProvider: Cancellable { /// - Parameters: /// - path: The location of the repository on disk, at which the repository /// has previously been created via `copyToWorkingDirectory`. - func openWorkingCopy(at path: AbsolutePath) throws -> WorkingCheckout + func openWorkingCopy(at path: AbsolutePath) async throws -> WorkingCheckout /// Copies the repository at path `from` to path `to`. /// - Parameters: diff --git a/Sources/SourceControl/RepositoryManager.swift b/Sources/SourceControl/RepositoryManager.swift index ae5a6d26fa2..b238d5b65bd 100644 --- a/Sources/SourceControl/RepositoryManager.swift +++ b/Sources/SourceControl/RepositoryManager.swift @@ -329,7 +329,7 @@ public class RepositoryManager: Cancellable { } } } - + // We are expecting handle.repository.url to always be a resolved absolute path. let shouldCacheLocalPackages = Environment.current["SWIFTPM_TESTS_PACKAGECACHE"] == "1" || cacheLocalPackages @@ -409,8 +409,8 @@ public class RepositoryManager: Cancellable { } /// Open a working copy checkout at a path - public func openWorkingCopy(at path: Basics.AbsolutePath) throws -> WorkingCheckout { - try self.provider.openWorkingCopy(at: path) + public func openWorkingCopy(at path: Basics.AbsolutePath) async throws -> WorkingCheckout { + try await self.provider.openWorkingCopy(at: path) } /// Validate a working copy check is aligned with its repository setup @@ -433,8 +433,8 @@ public class RepositoryManager: Cancellable { _ handle: RepositoryHandle, at destinationPath: Basics.AbsolutePath, editable: Bool - ) throws -> WorkingCheckout { - try self.provider.createWorkingCopy( + ) async throws -> WorkingCheckout { + try await self.provider.createWorkingCopy( repository: handle.repository, sourcePath: self.path.appending(handle.subpath), at: destinationPath, @@ -548,8 +548,8 @@ extension RepositoryManager { /// expected to be non-existent when called. /// /// - editable: The clone is expected to be edited by user. - public func createWorkingCopy(at path: Basics.AbsolutePath, editable: Bool) throws -> WorkingCheckout { - return try self.manager.createWorkingCopy(self, at: path, editable: editable) + public func createWorkingCopy(at path: Basics.AbsolutePath, editable: Bool) async throws -> WorkingCheckout { + return try await self.manager.createWorkingCopy(self, at: path, editable: editable) } } } diff --git a/Sources/Workspace/Workspace+Editing.swift b/Sources/Workspace/Workspace+Editing.swift index 2462a9a6bc2..0e2609aeedf 100644 --- a/Sources/Workspace/Workspace+Editing.swift +++ b/Sources/Workspace/Workspace+Editing.swift @@ -118,7 +118,7 @@ extension Workspace { throw WorkspaceDiagnostics.RevisionDoesNotExist(revision: revision.identifier) } - let workingCopy = try handle.createWorkingCopy(at: destination, editable: true) + let workingCopy = try await handle.createWorkingCopy(at: destination, editable: true) try workingCopy.checkout(revision: revision ?? checkoutState.revision) // Checkout to the new branch if provided. @@ -187,7 +187,7 @@ extension Workspace { let path = self.location.editSubdirectory(for: dependency) // Check for uncommitted and unpushed changes if force removal is off. if !forceRemove { - let workingCopy = try repositoryManager.openWorkingCopy(at: path) + let workingCopy = try await repositoryManager.openWorkingCopy(at: path) guard !workingCopy.hasUncommittedChanges() else { throw WorkspaceDiagnostics.UncommittedChanges(repositoryPath: path) } diff --git a/Sources/Workspace/Workspace+SourceControl.swift b/Sources/Workspace/Workspace+SourceControl.swift index 773c856e17a..6d6eb611176 100644 --- a/Sources/Workspace/Workspace+SourceControl.swift +++ b/Sources/Workspace/Workspace+SourceControl.swift @@ -50,7 +50,7 @@ extension Workspace { ) // Check out the given revision. - let workingCopy = try self.repositoryManager.openWorkingCopy(at: checkoutPath) + let workingCopy = try await self.repositoryManager.openWorkingCopy(at: checkoutPath) // Inform the delegate that we're about to start. delegate?.willCheckOut( @@ -147,7 +147,7 @@ extension Workspace { // This can become invalid if the build directory is moved. fetch: if self.fileSystem.isDirectory(checkoutPath) { // Fetch the checkout in case there are updates available. - let workingCopy = try self.repositoryManager.openWorkingCopy(at: checkoutPath) + let workingCopy = try await self.repositoryManager.openWorkingCopy(at: checkoutPath) // Ensure that the alternative object store is still valid. guard try self.repositoryManager.isValidWorkingCopy(workingCopy, for: repository) else { @@ -198,7 +198,7 @@ extension Workspace { let start = DispatchTime.now() // Create the working copy. - _ = try handle.createWorkingCopy(at: checkoutPath, editable: false) + _ = try await handle.createWorkingCopy(at: checkoutPath, editable: false) // Inform the delegate that we're done. let duration = start.distance(to: .now()) @@ -213,14 +213,14 @@ extension Workspace { } /// Removes the clone and checkout of the provided specifier. - func removeRepository(dependency: ManagedDependency) throws { + func removeRepository(dependency: ManagedDependency) async throws { guard case .sourceControlCheckout = dependency.state else { throw InternalError("cannot remove repository for \(dependency) with state \(dependency.state)") } // Remove the checkout. let dependencyPath = self.location.repositoriesCheckoutSubdirectory(for: dependency) - let workingCopy = try self.repositoryManager.openWorkingCopy(at: dependencyPath) + let workingCopy = try await self.repositoryManager.openWorkingCopy(at: dependencyPath) guard !workingCopy.hasUncommittedChanges() else { throw WorkspaceDiagnostics.UncommittedChanges(repositoryPath: dependencyPath) } diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 116c8e44bb8..60cbfe2ef91 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -1342,7 +1342,7 @@ extension Workspace { case .localSourceControl: break // NOOP case .remoteSourceControl: - try self.removeRepository(dependency: dependencyToRemove) + try await self.removeRepository(dependency: dependencyToRemove) case .registry: try self.removeRegistryArchive(for: dependencyToRemove) } diff --git a/Sources/_InternalTestSupport/InMemoryGitRepository.swift b/Sources/_InternalTestSupport/InMemoryGitRepository.swift index 6de8a0eec0a..f5c3d975981 100644 --- a/Sources/_InternalTestSupport/InMemoryGitRepository.swift +++ b/Sources/_InternalTestSupport/InMemoryGitRepository.swift @@ -281,7 +281,7 @@ extension InMemoryGitRepository: FileSystem { try self.head.fileSystem.createDirectory(path, recursive: recursive) } } - + public func createSymbolicLink(_ path: TSCAbsolutePath, pointingAt destination: TSCAbsolutePath, relative: Bool) throws { throw FileSystemError(.unsupported, path) } @@ -458,7 +458,7 @@ public final class InMemoryGitRepositoryProvider: RepositoryProvider { sourcePath: AbsolutePath, at destinationPath: AbsolutePath, editable: Bool - ) throws -> WorkingCheckout { + ) async throws -> WorkingCheckout { guard let checkout = fetchedMap[sourcePath] else { throw InternalError("unknown checkout at \(sourcePath)") } @@ -471,7 +471,7 @@ public final class InMemoryGitRepositoryProvider: RepositoryProvider { return checkoutsMap.contains(path) } - public func openWorkingCopy(at path: AbsolutePath) throws -> WorkingCheckout { + public func openWorkingCopy(at path: AbsolutePath) async throws -> WorkingCheckout { guard let checkout = checkoutsMap[path] else { throw InternalError("unknown checkout at \(path)") } diff --git a/Tests/SourceControlTests/GitRepositoryTests.swift b/Tests/SourceControlTests/GitRepositoryTests.swift index 511e2ff0194..af23196ad8c 100644 --- a/Tests/SourceControlTests/GitRepositoryTests.swift +++ b/Tests/SourceControlTests/GitRepositoryTests.swift @@ -302,9 +302,9 @@ class GitRepositoryTests: XCTestCase { } /// Test the handling of local checkouts. - func testCheckouts() throws { + func testCheckouts() async throws { try XCTSkipOnWindows(because: "https://github.com/swiftlang/swift-package-manager/issues/8564", skipSelfHostedCI: true) - try testWithTemporaryDirectory { path in + try await testWithTemporaryDirectory { path in // Create a test repository. let testRepoPath = path.appending("test-repo") try makeDirectories(testRepoPath) @@ -327,12 +327,12 @@ class GitRepositoryTests: XCTestCase { // Clone off a checkout. let checkoutPath = path.appending("checkout") - _ = try provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: checkoutPath, editable: false) + _ = try await provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: checkoutPath, editable: false) // The remote of this checkout should point to the clone. XCTAssertEqual(try GitRepository(path: checkoutPath).remotes()[0].url, testClonePath.pathString) let editsPath = path.appending("edit") - _ = try provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: editsPath, editable: true) + _ = try await provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: editsPath, editable: true) // The remote of this checkout should point to the original repo. XCTAssertEqual(try GitRepository(path: editsPath).remotes()[0].url, testRepoPath.pathString) @@ -349,9 +349,9 @@ class GitRepositoryTests: XCTestCase { } } - func testFetch() throws { + func testFetch() async throws { try XCTSkipOnWindows(because: "https://github.com/swiftlang/swift-package-manager/issues/8564", skipSelfHostedCI: true) - try testWithTemporaryDirectory { path in + try await testWithTemporaryDirectory { path in // Create a repo. let testRepoPath = path.appending("test-repo") try makeDirectories(testRepoPath) @@ -369,7 +369,7 @@ class GitRepositoryTests: XCTestCase { // Clone off a checkout. let checkoutPath = path.appending("checkout") - let checkoutRepo = try provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: checkoutPath, editable: false) + let checkoutRepo = try await provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: checkoutPath, editable: false) XCTAssertEqual(try checkoutRepo.getTags(), ["1.2.3"]) // Add a new file to original repo. @@ -389,9 +389,9 @@ class GitRepositoryTests: XCTestCase { } } - func testHasUnpushedCommits() throws { + func testHasUnpushedCommits() async throws { try XCTSkipOnWindows(because: "https://github.com/swiftlang/swift-package-manager/issues/8564", skipSelfHostedCI: true) - try testWithTemporaryDirectory { path in + try await testWithTemporaryDirectory { path in // Create a repo. let testRepoPath = path.appending("test-repo") try makeDirectories(testRepoPath) @@ -409,7 +409,7 @@ class GitRepositoryTests: XCTestCase { // Clone off a checkout. let checkoutPath = path.appending("checkout") - let checkoutRepo = try provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: checkoutPath, editable: true) + let checkoutRepo = try await provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: checkoutPath, editable: true) XCTAssertFalse(try checkoutRepo.hasUnpushedCommits()) // Add a new file to checkout. @@ -586,9 +586,9 @@ class GitRepositoryTests: XCTestCase { } } - func testSubmodules() throws { + func testSubmodules() async throws { try XCTSkipOnWindows(because: "https://github.com/swiftlang/swift-package-manager/issues/8564", skipSelfHostedCI: true) - try testWithTemporaryDirectory { path in + try await testWithTemporaryDirectory { path in let provider = GitRepositoryProvider() // Create repos: foo and bar, foo will have bar as submodule and then later @@ -615,7 +615,7 @@ class GitRepositoryTests: XCTestCase { // Fetch and clone repo foo. try provider.fetch(repository: fooSpecifier, to: fooRepoPath) - _ = try provider.createWorkingCopy(repository: fooSpecifier, sourcePath: fooRepoPath, at: fooWorkingPath, editable: false) + _ = try await provider.createWorkingCopy(repository: fooSpecifier, sourcePath: fooRepoPath, at: fooWorkingPath, editable: false) let fooRepo = GitRepository(path: fooRepoPath, isWorkingRepo: false) let fooWorkingRepo = GitRepository(path: fooWorkingPath) @@ -626,7 +626,7 @@ class GitRepositoryTests: XCTestCase { // Add submodule to foo and tag it as 1.0.1 try foo.checkout(newBranch: "submodule") - try AsyncProcess.checkNonZeroExit( + try await AsyncProcess.checkNonZeroExit( args: Git.tool, "-C", fooPath.pathString, "submodule", "add", barPath.pathString, "bar", environment: .init(Git.environmentBlock) ) @@ -648,7 +648,7 @@ class GitRepositoryTests: XCTestCase { // Add something to bar. try localFileSystem.writeFileContents(barPath.appending("bar.txt"), bytes: "hello") // Add a submodule too to check for recursive submodules. - try AsyncProcess.checkNonZeroExit( + try await AsyncProcess.checkNonZeroExit( args: Git.tool, "-C", barPath.pathString, "submodule", "add", bazPath.pathString, "baz", environment: .init(Git.environmentBlock) ) @@ -676,9 +676,9 @@ class GitRepositoryTests: XCTestCase { } } - func testAlternativeObjectStoreValidation() throws { + func testAlternativeObjectStoreValidation() async throws { try XCTSkipOnWindows(because: "https://github.com/swiftlang/swift-package-manager/issues/8564", skipSelfHostedCI: true) - try testWithTemporaryDirectory { path in + try await testWithTemporaryDirectory { path in // Create a repo. let testRepoPath = path.appending("test-repo") try makeDirectories(testRepoPath) @@ -696,7 +696,7 @@ class GitRepositoryTests: XCTestCase { // Clone off a checkout. let checkoutPath = path.appending("checkout") - let checkoutRepo = try provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: checkoutPath, editable: false) + let checkoutRepo = try await provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: checkoutPath, editable: false) // The object store should be valid. XCTAssertTrue(checkoutRepo.isAlternateObjectStoreValid(expected: testClonePath)) @@ -749,9 +749,9 @@ class GitRepositoryTests: XCTestCase { } } - func testMissingDefaultBranch() throws { + func testMissingDefaultBranch() async throws { try XCTSkipOnWindows(because: "https://github.com/swiftlang/swift-package-manager/issues/8564", skipSelfHostedCI: true) - try testWithTemporaryDirectory { path in + try await testWithTemporaryDirectory { path in // Create a repository. let testRepoPath = path.appending("test-repo") try makeDirectories(testRepoPath) @@ -775,7 +775,7 @@ class GitRepositoryTests: XCTestCase { // Clone off a checkout. let checkoutPath = path.appending("checkout") - let checkoutRepo = try provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: checkoutPath, editable: false) + let checkoutRepo = try await provider.createWorkingCopy(repository: repoSpec, sourcePath: testClonePath, at: checkoutPath, editable: false) XCTAssertNoSuchPath(checkoutPath.appending("file.swift")) // Try to check out the `main` branch. @@ -786,14 +786,14 @@ class GitRepositoryTests: XCTestCase { XCTAssertNoThrow(try checkoutRepo.getCurrentRevision()) } } - + func testValidDirectoryLocalRelativeOrigin() async throws { try XCTSkipOnWindows(because: "https://github.com/swiftlang/swift-package-manager/issues/8564", skipSelfHostedCI: true) try testWithTemporaryDirectory { tmpDir in // Create a repository. let packageDir = tmpDir.appending("SomePackage") try localFileSystem.createDirectory(packageDir) - + // Create a repository manager for it. let repoProvider = GitRepositoryProvider() let repositoryManager = RepositoryManager( @@ -802,26 +802,26 @@ class GitRepositoryTests: XCTestCase { provider: repoProvider, delegate: .none ) - + let customRemote = "../OriginOfSomePackage.git" - + // Before initializing the directory with a git repo, it is never valid. XCTAssertThrowsError(try repositoryManager.isValidDirectory(packageDir)) XCTAssertThrowsError(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(packageDir.pathString)))) XCTAssertThrowsError(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(customRemote)))) - + initGitRepo(packageDir) // Set the remote. try systemQuietly([Git.tool, "-C", packageDir.pathString, "remote", "add", "origin", customRemote]) XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir)) - + let customRemoteWithoutPathExtension = (customRemote as NSString).deletingPathExtension XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(customRemote)))) // We consider the directory valid even if the remote does not have the same path extension - in this case we expected '.git'. XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(customRemoteWithoutPathExtension)))) // We consider the directory valid even if the remote does not have the same path extension - in this case we expected '.git'. XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL((customRemote as NSString).deletingPathExtension + "/")))) - + // The following ensure that are actually checking the remote's origin. XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(path: AbsolutePath(validating: "/")))) XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL("/")))) @@ -829,18 +829,18 @@ class GitRepositoryTests: XCTestCase { XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(packageDir.pathString)))) XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(path: packageDir.appending(extension: "git")))) XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(packageDir.pathString.appending(".git"))))) - + XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL("https://mycustomdomain/some-package.git")))) } } - + func testValidDirectoryLocalAbsoluteOrigin() async throws { try XCTSkipOnWindows(because: "https://github.com/swiftlang/swift-package-manager/issues/8564", skipSelfHostedCI: true) try testWithTemporaryDirectory { tmpDir in // Create a repository. let packageDir = tmpDir.appending("SomePackage") try localFileSystem.createDirectory(packageDir) - + // Create a repository manager for it. let repoProvider = GitRepositoryProvider() let repositoryManager = RepositoryManager( @@ -849,19 +849,19 @@ class GitRepositoryTests: XCTestCase { provider: repoProvider, delegate: .none ) - + let customRemote = tmpDir.appending("OriginOfSomePackage.git") - + // Before initializing the directory with a git repo, it is never valid. XCTAssertThrowsError(try repositoryManager.isValidDirectory(packageDir)) XCTAssertThrowsError(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(packageDir.pathString)))) XCTAssertThrowsError(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(customRemote.pathString)))) - + initGitRepo(packageDir) // Set the remote. try systemQuietly([Git.tool, "-C", packageDir.pathString, "remote", "add", "origin", customRemote.pathString]) XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir)) - + let customRemotePath = customRemote.pathString let customRemotePathWithoutPathExtension = (customRemotePath as NSString).deletingPathExtension XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(path: customRemote))) @@ -872,7 +872,7 @@ class GitRepositoryTests: XCTestCase { // We consider the directory valid even if the remote does not have the same path extension - in this case we expected '.git'. XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(path: try AbsolutePath(validating: customRemotePathWithoutPathExtension + "/")))) XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL((customRemotePath as NSString).deletingPathExtension + "/")))) - + // The following ensure that are actually checking the remote's origin. XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(path: AbsolutePath(validating: "/")))) XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL("/")))) @@ -880,18 +880,18 @@ class GitRepositoryTests: XCTestCase { XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(packageDir.pathString)))) XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(path: packageDir.appending(extension: "git")))) XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(packageDir.pathString.appending(".git"))))) - + XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL("https://mycustomdomain/some-package.git")))) } } - + func testValidDirectoryRemoteOrigin() async throws { try XCTSkipOnWindows(because: "https://github.com/swiftlang/swift-package-manager/issues/8564", skipSelfHostedCI: true) try testWithTemporaryDirectory { tmpDir in // Create a repository. let packageDir = tmpDir.appending("SomePackage") try localFileSystem.createDirectory(packageDir) - + // Create a repository manager for it. let repoProvider = GitRepositoryProvider() let repositoryManager = RepositoryManager( @@ -900,24 +900,24 @@ class GitRepositoryTests: XCTestCase { provider: repoProvider, delegate: .none ) - + let customRemote = try XCTUnwrap(URL(string: "https://mycustomdomain/some-package.git")) - + // Before initializing the directory with a git repo, it is never valid. XCTAssertThrowsError(try repositoryManager.isValidDirectory(packageDir)) XCTAssertThrowsError(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(customRemote)))) - + initGitRepo(packageDir) // Set the remote. try systemQuietly([Git.tool, "-C", packageDir.pathString, "remote", "add", "origin", customRemote.absoluteString]) XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir)) - + XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL(customRemote)))) // We consider the directory valid even if the remote does not have the same path extension - in this case we expected '.git'. XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL("https://mycustomdomain/some-package")))) // We consider the directory valid even if the remote does not have the same path extension - in this case we expected '.git'. XCTAssertTrue(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL("https://mycustomdomain/some-package/")))) - + // The following ensure that are actually checking the remote's origin. XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(path: AbsolutePath(validating: "/")))) XCTAssertFalse(try repositoryManager.isValidDirectory(packageDir, for: RepositorySpecifier(url: SourceControlURL("/")))) diff --git a/Tests/SourceControlTests/InMemoryGitRepositoryTests.swift b/Tests/SourceControlTests/InMemoryGitRepositoryTests.swift index 416fac6164f..e3924389664 100644 --- a/Tests/SourceControlTests/InMemoryGitRepositoryTests.swift +++ b/Tests/SourceControlTests/InMemoryGitRepositoryTests.swift @@ -74,7 +74,7 @@ struct InMemoryGitRepositoryTests { } @Test - func provider() throws { + func provider() async throws { let v1 = "1.0.0" let v2 = "2.0.0" let repo = InMemoryGitRepository(path: .root, fs: InMemoryFileSystem()) @@ -103,9 +103,9 @@ struct InMemoryGitRepositoryTests { let fooCheckoutPath = AbsolutePath("/fooCheckout") #expect(!(try provider.workingCopyExists(at: fooCheckoutPath))) - _ = try provider.createWorkingCopy(repository: specifier, sourcePath: fooRepoPath, at: fooCheckoutPath, editable: false) + _ = try await provider.createWorkingCopy(repository: specifier, sourcePath: fooRepoPath, at: fooCheckoutPath, editable: false) #expect(try provider.workingCopyExists(at: fooCheckoutPath)) - let fooCheckout = try provider.openWorkingCopy(at: fooCheckoutPath) + let fooCheckout = try await provider.openWorkingCopy(at: fooCheckoutPath) #expect(try fooCheckout.getTags().sorted() == [v1, v2]) #expect(fooCheckout.exists(revision: try fooCheckout.getCurrentRevision())) diff --git a/Tests/SourceControlTests/RepositoryManagerTests.swift b/Tests/SourceControlTests/RepositoryManagerTests.swift index 81b32496df4..fd788eb2c6a 100644 --- a/Tests/SourceControlTests/RepositoryManagerTests.swift +++ b/Tests/SourceControlTests/RepositoryManagerTests.swift @@ -53,7 +53,7 @@ final class RepositoryManagerTests: XCTestCase { // Create a checkout of the repository. let checkoutPath = path.appending("checkout") - _ = try! handle.createWorkingCopy(at: checkoutPath, editable: false) + _ = try! await handle.createWorkingCopy(at: checkoutPath, editable: false) XCTAssertDirectoryExists(checkoutPath) XCTAssertFileExists(checkoutPath.appending("README.txt")) @@ -227,7 +227,7 @@ final class RepositoryManagerTests: XCTestCase { manager.reset(observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) - + XCTAssertTrue(!fs.isDirectory(repos)) try fs.createDirectory(repos, recursive: true) @@ -533,7 +533,7 @@ final class RepositoryManagerTests: XCTestCase { fatalError("should not be called") } - func createWorkingCopy(repository: RepositorySpecifier, sourcePath: AbsolutePath, at destinationPath: AbsolutePath, editable: Bool) throws -> WorkingCheckout { + func createWorkingCopy(repository: RepositorySpecifier, sourcePath: AbsolutePath, at destinationPath: AbsolutePath, editable: Bool) async throws -> WorkingCheckout { fatalError("should not be called") } @@ -541,7 +541,7 @@ final class RepositoryManagerTests: XCTestCase { fatalError("should not be called") } - func openWorkingCopy(at path: AbsolutePath) throws -> WorkingCheckout { + func openWorkingCopy(at path: AbsolutePath) async throws -> WorkingCheckout { fatalError("should not be called") } @@ -607,7 +607,7 @@ final class RepositoryManagerTests: XCTestCase { return MockRepository() } - func createWorkingCopy(repository: RepositorySpecifier, sourcePath: AbsolutePath, at destinationPath: AbsolutePath, editable: Bool) throws -> WorkingCheckout { + func createWorkingCopy(repository: RepositorySpecifier, sourcePath: AbsolutePath, at destinationPath: AbsolutePath, editable: Bool) async throws -> WorkingCheckout { fatalError("should not be called") } @@ -615,7 +615,7 @@ final class RepositoryManagerTests: XCTestCase { fatalError("should not be called") } - func openWorkingCopy(at path: AbsolutePath) throws -> WorkingCheckout { + func openWorkingCopy(at path: AbsolutePath) async throws -> WorkingCheckout { fatalError("should not be called") } @@ -763,17 +763,17 @@ private class DummyRepositoryProvider: RepositoryProvider { return DummyRepository(provider: self) } - func createWorkingCopy(repository: RepositorySpecifier, sourcePath: AbsolutePath, at destinationPath: AbsolutePath, editable: Bool) throws -> WorkingCheckout { + func createWorkingCopy(repository: RepositorySpecifier, sourcePath: AbsolutePath, at destinationPath: AbsolutePath, editable: Bool) async throws -> WorkingCheckout { try self.fileSystem.createDirectory(destinationPath) try self.fileSystem.writeFileContents(destinationPath.appending("README.txt"), bytes: "Hi") - return try self.openWorkingCopy(at: destinationPath) + return try await self.openWorkingCopy(at: destinationPath) } func workingCopyExists(at path: AbsolutePath) throws -> Bool { return false } - func openWorkingCopy(at path: AbsolutePath) throws -> WorkingCheckout { + func openWorkingCopy(at path: AbsolutePath) async throws -> WorkingCheckout { return DummyWorkingCheckout(at: path) } diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index f4772f88eda..08da995947a 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -2448,7 +2448,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkManagedDependencies { result in result.check(dependency: "bar", at: .edited(barPath)) } - let barRepo = try workspace.repositoryProvider.openWorkingCopy(at: barPath) as! InMemoryGitRepository + let barRepo = try await workspace.repositoryProvider.openWorkingCopy(at: barPath) as! InMemoryGitRepository XCTAssert(barRepo.revisions.contains("dev")) // Test unediting.