Skip to content

Commit 0787006

Browse files
authored
Throw fingerprint write errors (#3932)
Per review feedback, reverting part of the changes introduced in #3928.
1 parent 8beb947 commit 0787006

File tree

3 files changed

+90
-9
lines changed

3 files changed

+90
-9
lines changed

Sources/PackageRegistry/RegistryClient.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -432,10 +432,16 @@ public final class RegistryClient {
432432
switch storageResult {
433433
case .success:
434434
completion(.success(checksum))
435+
case .failure(PackageFingerprintStorageError.conflict(_, let existing)):
436+
switch self.fingerprintCheckingMode {
437+
case .strict:
438+
completion(.failure(RegistryError.checksumChanged(latest: checksum, previous: existing.value)))
439+
case .warn:
440+
observabilityScope.emit(warning: "The checksum \(checksum) from \(registry.url.absoluteString) does not match previously recorded value \(existing.value) from \(String(describing: existing.origin.url?.absoluteString))")
441+
completion(.success(checksum))
442+
}
435443
case .failure(let error):
436-
// Don't throw write errors
437-
observabilityScope.emit(warning: "Failed to save checksum '\(checksum) from \(registry.url) to fingerprints storage: \(error)")
438-
completion(.success(checksum))
444+
completion(.failure(error))
439445
}
440446
}
441447
} else {

Sources/Workspace/SourceControlPackageContainer.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,14 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri
185185
callback: $0
186186
)
187187
}
188-
} catch {
189-
observabilityScope.emit(warning: "Failed to save revision '\(revision.identifier) from \(sourceControlURL) to fingerprints storage: \(error)")
188+
} catch PackageFingerprintStorageError.conflict(_, let existing) {
189+
let message = "Revision \(revision.identifier) for \(self.package) version \(version) does not match previously recorded value \(existing.value) from \(String(describing: existing.origin.url?.absoluteString))"
190+
switch self.fingerprintCheckingMode {
191+
case .strict:
192+
throw StringError(message)
193+
case .warn:
194+
observabilityScope.emit(warning: message)
195+
}
190196
}
191197
} catch {
192198
self.observabilityScope.emit(error: "Failed to get source control fingerprint for \(self.package) version \(version) from storage: \(error)")

Tests/PackageRegistryTests/RegistryClientTests.swift

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,74 @@ final class RegistryClientTests: XCTestCase {
350350
var configuration = RegistryConfiguration()
351351
configuration.defaultRegistry = Registry(url: URL(string: registryURL)!)
352352

353+
let fingerprintStorage = MockPackageFingerprintStorage([
354+
identity: [
355+
version: [.registry: Fingerprint(origin: .registry(URL(string: registryURL)!), value: "non-matching checksum")],
356+
],
357+
])
358+
let registryClient = makeRegistryClient(configuration: configuration,
359+
httpClient: httpClient,
360+
fingerprintStorage: fingerprintStorage,
361+
fingerprintCheckingMode: .strict)
362+
363+
XCTAssertThrowsError(try registryClient.fetchSourceArchiveChecksum(package: identity, version: version)) { error in
364+
guard case RegistryError.checksumChanged = error else {
365+
return XCTFail("Expected RegistryError.checksumChanged, got \(error)")
366+
}
367+
}
368+
}
369+
370+
func testFetchSourceArchiveChecksum_storageConflict_fingerprintChecking_warn() throws {
371+
let registryURL = "https://packages.example.com"
372+
let identity = PackageIdentity.plain("mona.LinkedList")
373+
let (scope, name) = identity.scopeAndName!
374+
let version = Version("1.1.1")
375+
let metadataURL = URL(string: "\(registryURL)/\(scope)/\(name)/\(version)")!
376+
let checksum = "a2ac54cf25fbc1ad0028f03f0aa4b96833b83bb05a14e510892bb27dea4dc812"
377+
378+
let handler: HTTPClient.Handler = { request, _, completion in
379+
switch (request.method, request.url) {
380+
case (.get, metadataURL):
381+
XCTAssertEqual(request.headers.get("Accept").first, "application/vnd.swift.registry.v1+json")
382+
383+
let data = """
384+
{
385+
"id": "mona.LinkedList",
386+
"version": "1.1.1",
387+
"resources": [
388+
{
389+
"name": "source-archive",
390+
"type": "application/zip",
391+
"checksum": "\(checksum)"
392+
}
393+
],
394+
"metadata": {
395+
"description": "One thing links to another."
396+
}
397+
}
398+
""".data(using: .utf8)!
399+
400+
completion(.success(.init(
401+
statusCode: 200,
402+
headers: .init([
403+
.init(name: "Content-Length", value: "\(data.count)"),
404+
.init(name: "Content-Type", value: "application/json"),
405+
.init(name: "Content-Version", value: "1"),
406+
]),
407+
body: data
408+
)))
409+
default:
410+
completion(.failure(StringError("method and url should match")))
411+
}
412+
}
413+
414+
var httpClient = HTTPClient(handler: handler)
415+
httpClient.configuration.circuitBreakerStrategy = .none
416+
httpClient.configuration.retryStrategy = .none
417+
418+
var configuration = RegistryConfiguration()
419+
configuration.defaultRegistry = Registry(url: URL(string: registryURL)!)
420+
353421
let storedChecksum = "non-matching checksum"
354422
let fingerprintStorage = MockPackageFingerprintStorage([
355423
identity: [
@@ -359,21 +427,22 @@ final class RegistryClientTests: XCTestCase {
359427
let registryClient = makeRegistryClient(configuration: configuration,
360428
httpClient: httpClient,
361429
fingerprintStorage: fingerprintStorage,
362-
fingerprintCheckingMode: .strict)
430+
fingerprintCheckingMode: .warn)
363431

364432
let observability = ObservabilitySystem.makeForTesting()
365433

366-
// The checksum differs from that in storage, but we don't throw write errors
434+
// The checksum differs from that in storage, but error is not thrown
435+
// because fingerprintCheckingMode=.warn
367436
let checksumResponse = try registryClient.fetchSourceArchiveChecksum(
368437
package: identity,
369438
version: version,
370439
observabilityScope: observability.topScope
371440
)
372441
XCTAssertEqual(checksum, checksumResponse)
373442

374-
// There should be a warning though
443+
// But there should be a warning
375444
testDiagnostics(observability.diagnostics) { result in
376-
result.check(diagnostic: .contains("different from previously recorded value"), severity: .warning)
445+
result.check(diagnostic: .contains("does not match previously recorded value"), severity: .warning)
377446
}
378447

379448
// Storage should NOT be updated

0 commit comments

Comments
 (0)