Skip to content

Commit 6131cf8

Browse files
authored
remove SourceArchiver (#3874)
* remove SourceArchiver motivation: SourceArchiver was introduced in RegistryClient but adds a system depedency on bsdtar changes: * remove SourceArchiver * change RegistryClient to use ZipArchiver * add utility to strip first level of a directory and use that in RegistryClient * add and adjust tests
1 parent c313dea commit 6131cf8

File tree

8 files changed

+159
-93
lines changed

8 files changed

+159
-93
lines changed

Sources/Basics/FileSystem+Extensions.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import class Foundation.FileManager
1212
import struct Foundation.Data
13+
import struct Foundation.UUID
1314
import TSCBasic
1415

1516
// MARK: - user level
@@ -144,3 +145,22 @@ extension FileSystem {
144145
try self.createDirectory(path, recursive: true)
145146
}
146147
}
148+
149+
extension FileSystem {
150+
public func stripFirstLevel(of path: AbsolutePath) throws {
151+
let topLevelContents = try self.getDirectoryContents(path)
152+
guard topLevelContents.count == 1, let rootPath = topLevelContents.first.map({ path.appending(component: $0) }), self.isDirectory(rootPath) else {
153+
throw StringError("stripFirstLevel requires single top level directory")
154+
}
155+
156+
let tempDirectory = path.parentDirectory.appending(component: UUID().uuidString)
157+
try self.move(from: rootPath, to: tempDirectory)
158+
159+
let rootContents = try self.getDirectoryContents(tempDirectory)
160+
for entry in rootContents {
161+
try self.move(from: tempDirectory.appending(component: entry), to: path.appending(component: entry))
162+
}
163+
164+
try self.removeFileTree(tempDirectory)
165+
}
166+
}

Sources/PackageRegistry/CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
add_library(PackageRegistry
1010
Registry.swift
1111
RegistryConfiguration.swift
12-
RegistryClient.swift
13-
SourceArchiver.swift)
12+
RegistryClient.swift)
1413
target_link_libraries(PackageRegistry PUBLIC
1514
Basics
1615
PackageLoading

Sources/PackageRegistry/RegistryClient.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import PackageLoading
1818
import PackageModel
1919
import TSCBasic
2020
import protocol TSCUtility.Archiver
21+
import struct TSCUtility.ZipArchiver
2122

2223
/// Package registry client.
2324
/// API specification: https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md
@@ -55,7 +56,7 @@ public final class RegistryClient {
5556
{
5657
self.configuration = configuration
5758
self.identityResolver = identityResolver
58-
self.archiverProvider = customArchiverProvider ?? { fileSystem in SourceArchiver(fileSystem: fileSystem) }
59+
self.archiverProvider = customArchiverProvider ?? { fileSystem in ZipArchiver(fileSystem: fileSystem) }
5960
self.httpClient = customHTTPClient ?? HTTPClient()
6061
self.authorizationProvider = authorizationProvider
6162
self.jsonDecoder = JSONDecoder.makeWithDefaults()
@@ -426,7 +427,10 @@ public final class RegistryClient {
426427
// TODO: Bail if archive contains relative paths or overlapping files
427428
archiver.extract(from: downloadPath, to: destinationPath) { result in
428429
defer { try? fileSystem.removeFileTree(downloadPath) }
429-
completion(result.mapError { error in
430+
completion(result.tryMap {
431+
// strip first level component
432+
try fileSystem.stripFirstLevel(of: destinationPath)
433+
}.mapError { error in
430434
StringError("failed extracting '\(downloadPath)' to '\(destinationPath)': \(error)")
431435
})
432436
}

Sources/PackageRegistry/SourceArchiver.swift

Lines changed: 0 additions & 72 deletions
This file was deleted.

Sources/SPMTestSupport/MockArchiver.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import TSCBasic
1313
import TSCUtility
1414

1515
public class MockArchiver: Archiver {
16-
public typealias Handler = (MockArchiver, AbsolutePath, AbsolutePath, (Result<Void, Error>) -> Void) -> Void
16+
public typealias Handler = (MockArchiver, AbsolutePath, AbsolutePath, (Result<Void, Error>) -> Void) throws -> Void
1717

1818
public struct Extraction: Equatable {
1919
public let archivePath: AbsolutePath
@@ -38,11 +38,15 @@ public class MockArchiver: Archiver {
3838
to destinationPath: AbsolutePath,
3939
completion: @escaping (Result<Void, Error>) -> Void
4040
) {
41-
if let handler = self.handler {
42-
handler(self, archivePath, destinationPath, completion)
43-
} else {
44-
self.extractions.append(Extraction(archivePath: archivePath, destinationPath: destinationPath))
45-
completion(.success(()))
41+
do {
42+
if let handler = self.handler {
43+
try handler(self, archivePath, destinationPath, completion)
44+
} else {
45+
self.extractions.append(Extraction(archivePath: archivePath, destinationPath: destinationPath))
46+
completion(.success(()))
47+
}
48+
} catch {
49+
completion(.failure(error))
4650
}
4751
}
4852
}

Sources/SPMTestSupport/MockRegistry.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ private struct MockRegistryArchiver: Archiver {
279279
let rootPath = lines[1]
280280
for path in lines[2..<lines.count] {
281281
let relativePath = String(path.dropFirst(rootPath.count + 1))
282-
let targetPath = destinationPath.appending(RelativePath(relativePath))
282+
let targetPath = destinationPath.appending(component: "package").appending(RelativePath(relativePath))
283283
if !self.fileSystem.exists(targetPath.parentDirectory) {
284284
try self.fileSystem.createDirectory(targetPath.parentDirectory, recursive: true)
285285
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
12+
@testable import Basics
13+
import TSCBasic
14+
import XCTest
15+
import TSCTestSupport
16+
17+
final class FileSystemTests: XCTestCase {
18+
func testStripFirstLevelComponent() throws {
19+
let fileSystem = InMemoryFileSystem()
20+
21+
let rootPath = AbsolutePath("/root")
22+
try fileSystem.createDirectory(rootPath)
23+
24+
let totalDirectories = Int.random(in: 0 ..< 100)
25+
for index in 0 ..< totalDirectories {
26+
let path = rootPath.appending(component: "dir\(index)")
27+
try fileSystem.createDirectory(path, recursive: false)
28+
}
29+
30+
let totalFiles = Int.random(in: 0 ..< 100)
31+
for index in 0 ..< totalFiles {
32+
let path = rootPath.appending(component: "file\(index)")
33+
try fileSystem.writeFileContents(path, string: "\(index)")
34+
}
35+
36+
do {
37+
let contents = try fileSystem.getDirectoryContents(.root)
38+
XCTAssertEqual(contents.count, 1)
39+
}
40+
41+
try fileSystem.stripFirstLevel(of: .root)
42+
43+
do {
44+
let contents = Set(try fileSystem.getDirectoryContents(.root))
45+
XCTAssertEqual(contents.count, totalDirectories + totalFiles)
46+
47+
for index in 0 ..< totalDirectories {
48+
XCTAssertTrue(contents.contains("dir\(index)"))
49+
}
50+
for index in 0 ..< totalFiles {
51+
XCTAssertTrue(contents.contains("file\(index)"))
52+
}
53+
}
54+
}
55+
56+
func testStripFirstLevelComponentErrors() throws {
57+
do {
58+
let fileSystem = InMemoryFileSystem()
59+
XCTAssertThrowsError(try fileSystem.stripFirstLevel(of: .root), "expected error") { error in
60+
XCTAssertMatch((error as? StringError)?.description, .contains("requires single top level directory"))
61+
}
62+
}
63+
64+
do {
65+
let fileSystem = InMemoryFileSystem()
66+
for index in 0 ..< 3 {
67+
let path = AbsolutePath.root.appending(component: "dir\(index)")
68+
try fileSystem.createDirectory(path, recursive: false)
69+
}
70+
XCTAssertThrowsError(try fileSystem.stripFirstLevel(of: .root), "expected error") { error in
71+
XCTAssertMatch((error as? StringError)?.description, .contains("requires single top level directory"))
72+
}
73+
}
74+
75+
do {
76+
let fileSystem = InMemoryFileSystem()
77+
for index in 0 ..< 3 {
78+
let path = AbsolutePath.root.appending(component: "file\(index)")
79+
try fileSystem.writeFileContents(path, string: "\(index)")
80+
}
81+
XCTAssertThrowsError(try fileSystem.stripFirstLevel(of: .root), "expected error") { error in
82+
XCTAssertMatch((error as? StringError)?.description, .contains("requires single top level directory"))
83+
}
84+
}
85+
86+
do {
87+
let fileSystem = InMemoryFileSystem()
88+
let path = AbsolutePath.root.appending(component: "file")
89+
try fileSystem.writeFileContents(path, string: "")
90+
XCTAssertThrowsError(try fileSystem.stripFirstLevel(of: .root), "expected error") { error in
91+
XCTAssertMatch((error as? StringError)?.description, .contains("requires single top level directory"))
92+
}
93+
}
94+
}
95+
}

Tests/PackageRegistryTests/RegistryManagerTests.swift

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,17 @@ final class RegistryManagerTests: XCTestCase {
352352
let registryManager = RegistryClient(
353353
configuration: configuration,
354354
identityResolver: DefaultIdentityResolver(),
355-
customArchiverProvider: { _ in MockArchiver() },
355+
customArchiverProvider: { fileSystem in
356+
MockArchiver(handler: { _, from, to, callback in
357+
let data = try fileSystem.readFileContents(from)
358+
XCTAssertEqual(data, emptyZipFile)
359+
360+
let packagePath = to.appending(component: "package")
361+
try fileSystem.createDirectory(packagePath, recursive: true)
362+
try fileSystem.writeFileContents(packagePath.appending(component: "Package.swift"), string: "")
363+
callback(.success(()))
364+
})
365+
},
356366
customHTTPClient: httpClient
357367
)
358368

@@ -368,10 +378,8 @@ final class RegistryManagerTests: XCTestCase {
368378
checksumAlgorithm: checksumAlgorithm
369379
)
370380

371-
XCTAssertNoThrow {
372-
let data = try fileSystem.readFileContents(path)
373-
XCTAssertEqual(data, emptyZipFile)
374-
}
381+
let contents = try fileSystem.getDirectoryContents(path)
382+
XCTAssertEqual(contents, ["Package.swift"])
375383
}
376384

377385
func testDownloadSourceArchiveWithoutExpectedChecksumProvided() throws {
@@ -448,7 +456,17 @@ final class RegistryManagerTests: XCTestCase {
448456
let registryManager = RegistryClient(
449457
configuration: configuration,
450458
identityResolver: DefaultIdentityResolver(),
451-
customArchiverProvider: { _ in MockArchiver() },
459+
customArchiverProvider: { fileSystem in
460+
MockArchiver(handler: { _, from, to, callback in
461+
let data = try fileSystem.readFileContents(from)
462+
XCTAssertEqual(data, emptyZipFile)
463+
464+
let packagePath = to.appending(component: "package")
465+
try fileSystem.createDirectory(packagePath, recursive: true)
466+
try fileSystem.writeFileContents(packagePath.appending(component: "Package.swift"), string: "")
467+
callback(.success(()))
468+
})
469+
},
452470
customHTTPClient: httpClient
453471
)
454472

@@ -464,10 +482,8 @@ final class RegistryManagerTests: XCTestCase {
464482
checksumAlgorithm: checksumAlgorithm
465483
)
466484

467-
XCTAssertNoThrow {
468-
let data = try fileSystem.readFileContents(path)
469-
XCTAssertEqual(data, emptyZipFile)
470-
}
485+
let contents = try fileSystem.getDirectoryContents(path)
486+
XCTAssertEqual(contents, ["Package.swift"])
471487
}
472488

473489
func testLookupIdentities() throws {

0 commit comments

Comments
 (0)