Skip to content

Commit 4f45968

Browse files
authored
improve error message when Package.resolved cannot be loaded (#3721)
* improve error message when Package.resolved cannot be loaded motivation: provide clear and actionable information when trying to load pacakge.resolved file from a non-supported version, or otherwise malformed. changes: * add contextual information to error message * add few tests
1 parent 802db5d commit 4f45968

File tree

5 files changed

+101
-34
lines changed

5 files changed

+101
-34
lines changed

Sources/Basics/FileSystem+Extensions.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ extension FileSystem {
2121
}
2222
}
2323

24-
2524
// MARK: - cache
2625

2726
extension FileSystem {
@@ -119,7 +118,15 @@ extension FileSystem {
119118
return try Data(self.readFileContents(path).contents)
120119
}
121120

121+
public func readFileContents(_ path: AbsolutePath) throws -> String {
122+
return try String(decoding: self.readFileContents(path), as: UTF8.self)
123+
}
124+
122125
public func writeFileContents(_ path: AbsolutePath, data: Data) throws {
123-
return try self.writeFileContents(path, bytes: ByteString(data))
126+
return try self.writeFileContents(path, bytes: .init(data))
127+
}
128+
129+
public func writeFileContents(_ path: AbsolutePath, string: String) throws {
130+
return try self.writeFileContents(path, bytes: .init(encodingAsUTF8: string))
124131
}
125132
}

Sources/PackageGraph/PinsStore.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ fileprivate struct PinsStorage {
139139
partial[iterator.packageRef.identity] = iterator
140140
}
141141
default:
142-
throw InternalError("unknown RepositoryManager version: \(version)")
142+
throw StringError("unknown 'PinsStorage' version '\(version.version)' at '\(self.path)'.")
143143
}
144144
}
145145
}

Sources/SourceControl/RepositoryManager.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ public class RepositoryManager {
170170
// Make sure destination is free.
171171
try? self.fileSystem.removeFileTree(repositoryPath)
172172
let isCached = self.cachePath.map{ self.fileSystem.exists($0.appending(handle.subpath)) } ?? false
173-
173+
174174
// Inform delegate.
175175
queue.async {
176176
let details = FetchDetails(fromCache: isCached, updatedCache: false)
@@ -469,7 +469,7 @@ fileprivate struct RepositoryManagerStorage {
469469
let v1 = try self.decoder.decode(path: self.path, fileSystem: self.fileSystem, as: V1.self)
470470
return try v1.object.repositories.mapValues{ try .init($0, manager: manager) }
471471
default:
472-
throw InternalError("unknown RepositoryManager version: \(version)")
472+
throw StringError("unknown 'RepositoryManagerStorage' version '\(version.version)' at '\(self.path)'")
473473
}
474474
}
475475
}

Sources/Workspace/WorkspaceState.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ fileprivate struct WorkspaceStateStorage {
9898
let artifacts = v4.object.artifacts.map{ Workspace.ManagedArtifact($0) }
9999
return (dependencies: .init(dependencyMap: dependencyMap), artifacts: .init(artifacts))
100100
default:
101-
throw InternalError("unknown RepositoryManager version: \(version)")
101+
throw StringError("unknown 'WorkspaceStateStorage' version '\(version.version)' at '\(self.path)'")
102102
}
103103
}
104104
}

Tests/WorkspaceTests/PinsStoreTests.swift

Lines changed: 88 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -106,40 +106,100 @@ final class PinsStoreTests: XCTestCase {
106106
let fs = InMemoryFileSystem()
107107
let pinsFile = AbsolutePath("/pinsfile.txt")
108108

109-
try fs.writeFileContents(pinsFile) {
110-
$0 <<< """
111-
{
112-
"object": {
113-
"pins": [
114-
{
115-
"package": "Clang_C",
116-
"repositoryURL": "https://github.com/something/Clang_C.git",
117-
"state": {
118-
"branch": null,
119-
"revision": "90a9574276f0fd17f02f58979423c3fd4d73b59e",
120-
"version": "1.0.2",
121-
}
122-
},
123-
{
124-
"package": "Commandant",
125-
"repositoryURL": "https://github.com/something/Commandant.git",
126-
"state": {
127-
"branch": null,
128-
"revision": "c281992c31c3f41c48b5036c5a38185eaec32626",
129-
"version": "0.12.0"
130-
}
131-
}
132-
]
109+
try fs.writeFileContents(pinsFile, string:
110+
"""
111+
{
112+
"version": 1,
113+
"object": {
114+
"pins": [
115+
{
116+
"package": "Clang_C",
117+
"repositoryURL": "https://github.com/something/Clang_C.git",
118+
"state": {
119+
"branch": null,
120+
"revision": "90a9574276f0fd17f02f58979423c3fd4d73b59e",
121+
"version": "1.0.2",
122+
}
133123
},
134-
"version": 1
135-
}
136-
"""
137-
}
124+
{
125+
"package": "Commandant",
126+
"repositoryURL": "https://github.com/something/Commandant.git",
127+
"state": {
128+
"branch": null,
129+
"revision": "c281992c31c3f41c48b5036c5a38185eaec32626",
130+
"version": "0.12.0"
131+
}
132+
}
133+
]
134+
}
135+
}
136+
"""
137+
)
138138

139139
let store = try PinsStore(pinsFile: pinsFile, workingDirectory: .root, fileSystem: fs, mirrors: .init())
140140
XCTAssertEqual(store.pinsMap.keys.map { $0.description }.sorted(), ["clang_c", "commandant"])
141141
}
142142

143+
func testLoadingSchema2() throws {
144+
let fs = InMemoryFileSystem()
145+
let pinsFile = AbsolutePath("/pinsfile.txt")
146+
147+
try fs.writeFileContents(pinsFile, string:
148+
"""
149+
{
150+
"version": 2,
151+
"pins": [
152+
{
153+
"identity": "clang_c",
154+
"location": "https://github.com/something/Clang_C.git",
155+
"state": {
156+
"branch": null,
157+
"revision": "90a9574276f0fd17f02f58979423c3fd4d73b59e",
158+
"version": "1.0.2",
159+
}
160+
},
161+
{
162+
"identity": "commandant",
163+
"location": "https://github.com/something/Commandant.git",
164+
"state": {
165+
"branch": null,
166+
"revision": "c281992c31c3f41c48b5036c5a38185eaec32626",
167+
"version": "0.12.0"
168+
}
169+
}
170+
]
171+
}
172+
"""
173+
)
174+
175+
let store = try PinsStore(pinsFile: pinsFile, workingDirectory: .root, fileSystem: fs, mirrors: .init())
176+
XCTAssertEqual(store.pinsMap.keys.map { $0.description }.sorted(), ["clang_c", "commandant"])
177+
}
178+
179+
func testLoadingUnknownSchemaVersion() throws {
180+
let fs = InMemoryFileSystem()
181+
let pinsFile = AbsolutePath("/pinsfile.txt")
182+
183+
let version = -1
184+
try fs.writeFileContents(pinsFile, string: "{ \"version\": \(version) }");
185+
186+
XCTAssertThrowsError(try PinsStore(pinsFile: pinsFile, workingDirectory: .root, fileSystem: fs, mirrors: .init()), "error expected", { error in
187+
XCTAssertEqual("\(error)", "Package.resolved file is corrupted or malformed; fix or delete the file to continue: unknown 'PinsStorage' version '\(version)' at '\(pinsFile)'.")
188+
})
189+
190+
}
191+
192+
func testLoadingBadFormat() throws {
193+
let fs = InMemoryFileSystem()
194+
let pinsFile = AbsolutePath("/pinsfile.txt")
195+
196+
try fs.writeFileContents(pinsFile, string: "boom")
197+
198+
XCTAssertThrowsError(try PinsStore(pinsFile: pinsFile, workingDirectory: .root, fileSystem: fs, mirrors: .init()), "error expected", { error in
199+
XCTAssertMatch("\(error)", .contains("Package.resolved file is corrupted or malformed; fix or delete the file to continue"))
200+
})
201+
}
202+
143203
func testEmptyPins() throws {
144204
let fs = InMemoryFileSystem()
145205
let pinsFile = AbsolutePath("/pinsfile.txt")

0 commit comments

Comments
 (0)