Skip to content

Commit f84ad62

Browse files
authored
Merge branch 'main' into luis-gustavo/1245-package-collections-keywords-result-page
2 parents 02bdcda + 8b8b2d3 commit f84ad62

35 files changed

+673
-553
lines changed

Package.resolved

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Resources/ChartData/rfs6-errors.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,4 @@
254254
}
255255
]
256256
}
257-
]
257+
]

Sources/App/Commands/Analyze.swift

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,26 @@ extension Analyze {
7171

7272

7373
static func trimCheckouts() throws {
74+
@Dependency(\.fileManager) var fileManager
7475
let checkoutDir = URL(
75-
fileURLWithPath: Current.fileManager.checkoutsDirectory(),
76+
fileURLWithPath: fileManager.checkoutsDirectory(),
7677
isDirectory: true
7778
)
78-
try Current.fileManager.contentsOfDirectory(atPath: checkoutDir.path)
79+
try fileManager.contentsOfDirectory(atPath: checkoutDir.path)
7980
.map { dir -> (String, Date)? in
8081
let url = checkoutDir.appendingPathComponent(dir)
81-
guard let mod = try Current.fileManager
82+
guard let mod = try fileManager
8283
.attributesOfItem(atPath: url.path)[.modificationDate] as? Date
8384
else { return nil }
8485
return (url.path, mod)
8586
}
8687
.forEach { pair in
8788
guard let (path, mod) = pair else { return }
8889
@Dependency(\.date.now) var now
90+
@Dependency(\.fileManager) var fileManager
8991
let cutoff = now.addingTimeInterval(-Constants.gitCheckoutMaxAge)
9092
if mod < cutoff {
91-
try Current.fileManager.removeItem(atPath: path)
93+
try fileManager.removeItem(atPath: path)
9294
AppMetrics.analyzeTrimCheckoutsCount?.inc()
9395
}
9496
}
@@ -139,9 +141,10 @@ extension Analyze {
139141
AppMetrics.analyzeCandidatesCount?.set(packages.count)
140142

141143
// get or create directory
142-
let checkoutDir = Current.fileManager.checkoutsDirectory()
144+
@Dependency(\.fileManager) var fileManager
145+
let checkoutDir = fileManager.checkoutsDirectory()
143146
Current.logger().info("Checkout directory: \(checkoutDir)")
144-
if !Current.fileManager.fileExists(atPath: checkoutDir) {
147+
if !fileManager.fileExists(atPath: checkoutDir) {
145148
try await createCheckoutsDirectory(client: client, path: checkoutDir)
146149
}
147150

@@ -234,9 +237,10 @@ extension Analyze {
234237
path: String) async throws {
235238
Current.logger().info("Creating checkouts directory at path: \(path)")
236239
do {
237-
try Current.fileManager.createDirectory(atPath: path,
238-
withIntermediateDirectories: false,
239-
attributes: nil)
240+
@Dependency(\.fileManager) var fileManager
241+
try fileManager.createDirectory(atPath: path,
242+
withIntermediateDirectories: false,
243+
attributes: nil)
240244
} catch {
241245
let error = AppError.genericError(nil, "Failed to create checkouts directory: \(error.localizedDescription)")
242246
Current.logger().report(error: error)
@@ -251,8 +255,9 @@ extension Analyze {
251255
/// - Throws: Shell errors
252256
static func clone(cacheDir: String, url: String) async throws {
253257
Current.logger().info("cloning \(url) to \(cacheDir)")
258+
@Dependency(\.fileManager) var fileManager
254259
try await Current.shell.run(command: .gitClone(url: URL(string: url)!, to: cacheDir),
255-
at: Current.fileManager.checkoutsDirectory())
260+
at: fileManager.checkoutsDirectory())
256261
}
257262

258263

@@ -263,11 +268,12 @@ extension Analyze {
263268
/// - url: url to fetch from
264269
/// - Throws: Shell errors
265270
static func fetch(cacheDir: String, branch: String, url: String) async throws {
271+
@Dependency(\.fileManager) var fileManager
266272
Current.logger().info("pulling \(url) in \(cacheDir)")
267273
// clean up stray lock files that might have remained from aborted commands
268274
for fileName in ["HEAD.lock", "index.lock"] {
269275
let filePath = cacheDir + "/.git/\(fileName)"
270-
if Current.fileManager.fileExists(atPath: filePath) {
276+
if fileManager.fileExists(atPath: filePath) {
271277
Current.logger().info("Removing stale \(fileName) at path: \(filePath)")
272278
try await Current.shell.run(command: .removeFile(from: filePath))
273279
}
@@ -286,12 +292,13 @@ extension Analyze {
286292
/// - Parameters:
287293
/// - package: `Package` to refresh
288294
static func refreshCheckout(package: Joined<Package, Repository>) async throws {
289-
guard let cacheDir = Current.fileManager.cacheDirectoryPath(for: package.model) else {
295+
@Dependency(\.fileManager) var fileManager
296+
guard let cacheDir = fileManager.cacheDirectoryPath(for: package.model) else {
290297
throw AppError.invalidPackageCachePath(package.model.id, package.model.url)
291298
}
292299

293300
do {
294-
guard Current.fileManager.fileExists(atPath: cacheDir) else {
301+
guard fileManager.fileExists(atPath: cacheDir) else {
295302
try await clone(cacheDir: cacheDir, url: package.model.url)
296303
return
297304
}
@@ -322,7 +329,8 @@ extension Analyze {
322329
guard let repo = package.repository else {
323330
throw AppError.genericError(package.model.id, "updateRepository: no repository")
324331
}
325-
guard let gitDirectory = Current.fileManager.cacheDirectoryPath(for: package.model) else {
332+
@Dependency(\.fileManager) var fileManager
333+
guard let gitDirectory = fileManager.cacheDirectoryPath(for: package.model) else {
326334
throw AppError.invalidPackageCachePath(package.model.id, package.model.url)
327335
}
328336

@@ -375,7 +383,8 @@ extension Analyze {
375383
/// - Returns: future with incoming `Version`s
376384
static func getIncomingVersions(client: Client,
377385
package: Joined<Package, Repository>) async throws -> [Version] {
378-
guard let cacheDir = Current.fileManager.cacheDirectoryPath(for: package.model) else {
386+
@Dependency(\.fileManager) var fileManager
387+
guard let cacheDir = fileManager.cacheDirectoryPath(for: package.model) else {
379388
throw AppError.invalidPackageCachePath(package.model.id, package.model.url)
380389
}
381390

@@ -523,7 +532,8 @@ extension Analyze {
523532
/// - Throws: Shell errors or AppError.invalidRevision if there is no Package.swift file
524533
/// - Returns: `Manifest` data
525534
static func dumpPackage(at path: String) async throws -> Manifest {
526-
guard Current.fileManager.fileExists(atPath: path + "/Package.swift") else {
535+
@Dependency(\.fileManager) var fileManager
536+
guard fileManager.fileExists(atPath: path + "/Package.swift") else {
527537
// It's important to check for Package.swift - otherwise `dump-package` will go
528538
// up the tree through parent directories to find one
529539
throw AppError.invalidRevision(nil, "no Package.swift")
@@ -546,7 +556,8 @@ extension Analyze {
546556
/// - Returns: `Result` with `Manifest` data
547557
static func getPackageInfo(package: Joined<Package, Repository>, version: Version) async throws -> PackageInfo {
548558
// check out version in cache directory
549-
guard let cacheDir = Current.fileManager.cacheDirectoryPath(for: package.model) else {
559+
@Dependency(\.fileManager) var fileManager
560+
guard let cacheDir = fileManager.cacheDirectoryPath(for: package.model) else {
550561
throw AppError.invalidPackageCachePath(package.model.id,
551562
package.model.url)
552563
}

Sources/App/Commands/ReAnalyzeVersions.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,9 @@ enum ReAnalyzeVersions {
183183
try await withEscapedDependencies { dependencies in
184184
try await database.transaction { tx in
185185
try await dependencies.yield {
186-
guard let cacheDir = Current.fileManager.cacheDirectoryPath(for: pkg.model) else { return }
187-
if !Current.fileManager.fileExists(atPath: cacheDir) || refreshCheckouts {
186+
@Dependency(\.fileManager) var fileManager
187+
guard let cacheDir = fileManager.cacheDirectoryPath(for: pkg.model) else { return }
188+
if !fileManager.fileExists(atPath: cacheDir) || refreshCheckouts {
188189
try await Analyze.refreshCheckout(package: pkg)
189190
}
190191

Sources/App/Core/AppEnvironment.swift

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import FoundationNetworking
2323

2424

2525
struct AppEnvironment: Sendable {
26-
var fileManager: FileManager
2726
var git: Git
2827
var logger: @Sendable () -> Logger
2928
var setLogger: @Sendable (Logger) -> Void
@@ -35,7 +34,6 @@ extension AppEnvironment {
3534
nonisolated(unsafe) static var logger: Logger!
3635

3736
static let live = AppEnvironment(
38-
fileManager: .live,
3937
git: .live,
4038
logger: { logger },
4139
setLogger: { logger in Self.logger = logger },
@@ -44,52 +42,6 @@ extension AppEnvironment {
4442
}
4543

4644

47-
struct FileManager: Sendable {
48-
var attributesOfItem: @Sendable (_ path: String) throws -> [FileAttributeKey : Any]
49-
var contentsOfDirectory: @Sendable (_ path: String) throws -> [String]
50-
var contents: @Sendable (_ atPath: String) -> Data?
51-
var checkoutsDirectory: @Sendable () -> String
52-
var createDirectory: @Sendable (String, Bool, [FileAttributeKey : Any]?) throws -> Void
53-
var fileExists: @Sendable (String) -> Bool
54-
var removeItem: @Sendable (_ path: String) throws -> Void
55-
var workingDirectory: @Sendable () -> String
56-
57-
// pass-through methods to preserve argument labels
58-
func attributesOfItem(atPath path: String) throws -> [FileAttributeKey : Any] {
59-
try attributesOfItem(path)
60-
}
61-
func contents(atPath path: String) -> Data? { contents(path) }
62-
func contentsOfDirectory(atPath path: String) throws -> [String] {
63-
try contentsOfDirectory(path)
64-
}
65-
func createDirectory(atPath path: String,
66-
withIntermediateDirectories createIntermediates: Bool,
67-
attributes: [FileAttributeKey : Any]?) throws {
68-
try createDirectory(path, createIntermediates, attributes)
69-
}
70-
func fileExists(atPath path: String) -> Bool { fileExists(path) }
71-
func removeItem(atPath path: String) throws { try removeItem(path) }
72-
73-
static let live: Self = .init(
74-
attributesOfItem: { try Foundation.FileManager.default.attributesOfItem(atPath: $0) },
75-
contentsOfDirectory: { try Foundation.FileManager.default.contentsOfDirectory(atPath: $0) },
76-
contents: { Foundation.FileManager.default.contents(atPath: $0) },
77-
checkoutsDirectory: { Environment.get("CHECKOUTS_DIR") ?? DirectoryConfiguration.detect().workingDirectory + "SPI-checkouts" },
78-
createDirectory: { try Foundation.FileManager.default.createDirectory(atPath: $0, withIntermediateDirectories: $1, attributes: $2) },
79-
fileExists: { Foundation.FileManager.default.fileExists(atPath: $0) },
80-
removeItem: { try Foundation.FileManager.default.removeItem(atPath: $0) },
81-
workingDirectory: { DirectoryConfiguration.detect().workingDirectory }
82-
)
83-
}
84-
85-
86-
extension FileManager {
87-
func cacheDirectoryPath(for package: Package) -> String? {
88-
guard let dirname = package.cacheDirectoryName else { return nil }
89-
return checkoutsDirectory() + "/" + dirname
90-
}
91-
}
92-
9345

9446
struct Git: Sendable {
9547
var commitCount: @Sendable (String) async throws -> Int
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Foundation
16+
17+
import Dependencies
18+
import DependenciesMacros
19+
import IssueReporting
20+
import Vapor
21+
22+
23+
@DependencyClient
24+
struct FileManagerClient {
25+
var attributesOfItem: @Sendable (_ atPath: String) throws -> [FileAttributeKey : Any]
26+
var checkoutsDirectory: @Sendable () -> String = { reportIssue("checkoutsDirectory"); return "SPI-checkouts" }
27+
var contents: @Sendable (_ atPath: String) -> Data?
28+
var contentsOfDirectory: @Sendable (_ atPath: String) throws -> [String]
29+
var createDirectory: @Sendable (_ atPath: String, _ withIntermediateDirectories: Bool, _ attributes: [FileAttributeKey : Any]?) throws -> Void
30+
var fileExists: @Sendable (_ atPath: String) -> Bool = { reportIssue("fileExists"); return Foundation.FileManager.default.fileExists(atPath: $0) }
31+
var removeItem: @Sendable (_ atPath: String) throws -> Void
32+
var workingDirectory: @Sendable () -> String = { reportIssue("workingDirectory"); return "" }
33+
}
34+
35+
36+
extension FileManagerClient {
37+
func cacheDirectoryPath(for package: Package) -> String? {
38+
guard let dirname = package.cacheDirectoryName else { return nil }
39+
return checkoutsDirectory() + "/" + dirname
40+
}
41+
}
42+
43+
44+
extension FileManagerClient: DependencyKey {
45+
static var liveValue: Self {
46+
.init(
47+
attributesOfItem: { try Foundation.FileManager.default.attributesOfItem(atPath: $0) },
48+
checkoutsDirectory: { Environment.get("CHECKOUTS_DIR") ?? DirectoryConfiguration.detect().workingDirectory + "SPI-checkouts" },
49+
contents: { Foundation.FileManager.default.contents(atPath: $0) },
50+
contentsOfDirectory: { try Foundation.FileManager.default.contentsOfDirectory(atPath: $0) },
51+
createDirectory: { try Foundation.FileManager.default.createDirectory(atPath: $0, withIntermediateDirectories: $1, attributes: $2) },
52+
fileExists: { Foundation.FileManager.default.fileExists(atPath: $0) },
53+
removeItem: { try Foundation.FileManager.default.removeItem(atPath: $0) },
54+
workingDirectory: { DirectoryConfiguration.detect().workingDirectory }
55+
)
56+
}
57+
}
58+
59+
60+
extension FileManagerClient: TestDependencyKey {
61+
static var testValue: Self {
62+
var mock = Self()
63+
// Override the `unimplemented` default because it is a very common dependency.
64+
mock.checkoutsDirectory = { "SPI-checkouts" }
65+
mock.workingDirectory = { DirectoryConfiguration.detect().workingDirectory }
66+
return mock
67+
}
68+
}
69+
70+
71+
extension DependencyValues {
72+
var fileManager: FileManagerClient {
73+
get { self[FileManagerClient.self] }
74+
set { self[FileManagerClient.self] = newValue }
75+
}
76+
}

Sources/App/Core/Emoji.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
// limitations under the License.
1414

1515
import Foundation
16+
17+
import Dependencies
1618
import Vapor
1719

1820

@@ -33,8 +35,8 @@ struct EmojiStorage {
3335
var regularExpression: NSRegularExpression?
3436

3537
init() {
36-
let pathToEmojiFile = Current.fileManager.workingDirectory()
37-
.appending("Resources/emoji.json")
38+
@Dependency(\.fileManager) var fileManager
39+
let pathToEmojiFile = fileManager.workingDirectory().appending("Resources/emoji.json")
3840

3941
lookup = [:]
4042
regularExpression = nil

Sources/App/Core/Extensions/Badge.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import Vapor
1615
import Foundation
1716

17+
import Dependencies
18+
import Vapor
19+
1820

1921
struct Badge: Content, Equatable {
2022
var schemaVersion: Int
@@ -68,8 +70,8 @@ enum BadgeType: String, Codable {
6870
extension Badge {
6971

7072
static private func loadSVGLogo() -> String? {
71-
let pathToFile = Current.fileManager.workingDirectory()
72-
.appending("Public/images/logo-tiny.svg")
73+
@Dependency(\.fileManager) var fileManager
74+
let pathToFile = fileManager.workingDirectory().appending("Public/images/logo-tiny.svg")
7375

7476
return try? String(contentsOfFile: pathToFile, encoding: .utf8)
7577
}

Sources/App/Core/PackageCollection+signing.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,12 @@ extension SignedCollection {
6161
return true
6262
}
6363

64-
static let certsDir = URL(fileURLWithPath: Current.fileManager.workingDirectory())
65-
.appendingPathComponent("Resources")
66-
.appendingPathComponent("Certs")
64+
static var certsDir: URL {
65+
@Dependency(\.fileManager) var fileManager
66+
return URL(fileURLWithPath: fileManager.workingDirectory())
67+
.appendingPathComponent("Resources")
68+
.appendingPathComponent("Certs")
69+
}
6770

6871
static let signer = PackageCollectionSigning(
6972
trustedRootCertsDir: certsDir,

0 commit comments

Comments
 (0)