Skip to content

Commit 30277bd

Browse files
committed
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 0419392 commit 30277bd

File tree

5 files changed

+105
-33
lines changed

5 files changed

+105
-33
lines changed

Sources/Basics/FileSystem+Extensions.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import class Foundation.FileManager
1212
import struct Foundation.Data
1313
import TSCBasic
14+
import PackageDescription
1415

1516
// MARK: - user level
1617

@@ -118,8 +119,19 @@ extension FileSystem {
118119
public func readFileContents(_ path: AbsolutePath) throws -> Data {
119120
return try Data(self.readFileContents(path).contents)
120121
}
121-
122+
123+
public func readFileContents(_ path: AbsolutePath) throws -> String {
124+
guard let string = try String(data: self.readFileContents(path), encoding: .utf8) else {
125+
throw StringError("invalid UTF8 string")
126+
}
127+
return string
128+
}
129+
122130
public func writeFileContents(_ path: AbsolutePath, data: Data) throws {
123-
return try self.writeFileContents(path, bytes: ByteString(data))
131+
return try self.writeFileContents(path, bytes: .init(data))
132+
}
133+
134+
public func writeFileContents(_ path: AbsolutePath, string: String) throws {
135+
return try self.writeFileContents(path, bytes: .init(encodingAsUTF8: string))
124136
}
125137
}

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 '\(Self.self)' version '\(version.version)' at '\(self.path)'.")
143143
}
144144
}
145145
}

Sources/SourceControl/RepositoryManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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 '\(Self.self)' 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 '\(Self.self)' 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)