Skip to content

Commit 6befd7f

Browse files
authored
Ensure checkouts get updated when enforcing resolved versions (#4114)
Previously there could be cases where an existing checkout won't be updated from the remote even if `--skip-update` hasn't been passed.
1 parent 968156e commit 6befd7f

File tree

3 files changed

+103
-1
lines changed

3 files changed

+103
-1
lines changed

Sources/SPMTestSupport/GitRepositoryExtensions.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ public extension GitRepository {
2828
args: Git.tool, "-C", path.pathString, "rev-parse", "--abbrev-ref", "HEAD").spm_chomp()
2929
}
3030

31+
/// Returns the revision for a given tag.
32+
func revision(forTag tag: String) throws -> String {
33+
return try Process.checkNonZeroExit(
34+
args: Git.tool, "-C", path.pathString, "rev-parse", tag).spm_chomp()
35+
}
36+
3137
/// Stage a file.
3238
func stage(file: String) throws {
3339
try systemQuietly([Git.tool, "-C", self.path.pathString, "add", file])

Sources/Workspace/Workspace.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2753,9 +2753,14 @@ extension Workspace {
27532753
//
27542754
// We just request the packages here, repository manager will
27552755
// automatically manage the parallelism.
2756+
let group = DispatchGroup()
27562757
for pin in pinsStore.pins {
2757-
packageContainerProvider.getContainer(for: pin.packageRef, skipUpdate: true, observabilityScope: observabilityScope, on: .sharedConcurrent, completion: { _ in })
2758+
group.enter()
2759+
packageContainerProvider.getContainer(for: pin.packageRef, skipUpdate: self.configuration.skipDependenciesUpdates, observabilityScope: observabilityScope, on: .sharedConcurrent, completion: { _ in
2760+
group.leave()
2761+
})
27582762
}
2763+
group.wait()
27592764

27602765
// Compute the pins that we need to actually clone.
27612766
//

Tests/CommandsTests/PackageToolTests.swift

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,97 @@ final class PackageToolTests: CommandsTestCase {
986986
}
987987
}
988988

989+
func testOnlyUseVersionsFromResolvedFileFetchesWithExistingState() throws {
990+
func writeResolvedFile(packageDir: AbsolutePath, repositoryURL: String, revision: String, version: String) throws {
991+
try localFileSystem.writeFileContents(packageDir.appending(component: "Package.resolved")) {
992+
$0 <<< """
993+
{
994+
"object": {
995+
"pins": [
996+
{
997+
"package": "library",
998+
"repositoryURL": "\(repositoryURL)",
999+
"state": {
1000+
"branch": null,
1001+
"revision": "\(revision)",
1002+
"version": "\(version)"
1003+
}
1004+
}
1005+
]
1006+
},
1007+
"version": 1
1008+
}
1009+
"""
1010+
}
1011+
}
1012+
1013+
try testWithTemporaryDirectory { tmpPath in
1014+
let packageDir = tmpPath.appending(components: "library")
1015+
try localFileSystem.writeFileContents(packageDir.appending(component: "Package.swift")) {
1016+
$0 <<< """
1017+
// swift-tools-version:5.0
1018+
import PackageDescription
1019+
let package = Package(
1020+
name: "library",
1021+
products: [ .library(name: "library", targets: ["library"]) ],
1022+
targets: [ .target(name: "library") ]
1023+
)
1024+
"""
1025+
}
1026+
try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "library", "library.swift")) {
1027+
$0 <<< """
1028+
public func Foo() { }
1029+
"""
1030+
}
1031+
1032+
let depGit = GitRepository(path: packageDir)
1033+
try depGit.create()
1034+
try depGit.stageEverything()
1035+
try depGit.commit()
1036+
try depGit.tag(name: "1.0.0")
1037+
1038+
let initialRevision = try depGit.revision(forTag: "1.0.0")
1039+
let repositoryURL = "file://\(packageDir.pathString)"
1040+
1041+
let clientDir = tmpPath.appending(components: "client")
1042+
try localFileSystem.writeFileContents(clientDir.appending(component: "Package.swift")) {
1043+
$0 <<< """
1044+
// swift-tools-version:5.0
1045+
import PackageDescription
1046+
let package = Package(
1047+
name: "client",
1048+
dependencies: [ .package(url: "\(repositoryURL)", from: "1.0.0") ],
1049+
targets: [ .target(name: "client", dependencies: [ "library" ]) ]
1050+
)
1051+
"""
1052+
}
1053+
try localFileSystem.writeFileContents(clientDir.appending(components: "Sources", "client", "main.swift")) {
1054+
$0 <<< """
1055+
print("hello")
1056+
"""
1057+
}
1058+
1059+
// Initial resolution with clean state.
1060+
try writeResolvedFile(packageDir: clientDir, repositoryURL: repositoryURL, revision: initialRevision, version: "1.0.0")
1061+
_ = try execute(["resolve", "--only-use-versions-from-resolved-file"], packagePath: clientDir)
1062+
1063+
// Make a change to the dependency and tag a new version.
1064+
try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "library", "library.swift")) {
1065+
$0 <<< """
1066+
public func Best() { }
1067+
"""
1068+
}
1069+
try depGit.stageEverything()
1070+
try depGit.commit()
1071+
try depGit.tag(name: "1.0.1")
1072+
let updatedRevision = try depGit.revision(forTag: "1.0.1")
1073+
1074+
// Require new version but re-use existing state that hasn't fetched the latest revision, yet.
1075+
try writeResolvedFile(packageDir: clientDir, repositoryURL: repositoryURL, revision: updatedRevision, version: "1.0.1")
1076+
_ = try execute(["resolve", "--only-use-versions-from-resolved-file"], packagePath: clientDir)
1077+
}
1078+
}
1079+
9891080
func testSymlinkedDependency() throws {
9901081
try testWithTemporaryDirectory { path in
9911082
let fs = localFileSystem

0 commit comments

Comments
 (0)