Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions Sources/App/Commands/Analyze.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,10 @@ extension Analyze {
.forEach { pair in
guard let (path, mod) = pair else { return }
@Dependency(\.date.now) var now
@Dependency(\.fileManager) var fileManager
let cutoff = now.addingTimeInterval(-Constants.gitCheckoutMaxAge)
if mod < cutoff {
try Current.fileManager.removeItem(atPath: path)
try fileManager.removeItem(atPath: path)
AppMetrics.analyzeTrimCheckoutsCount?.inc()
}
}
Expand Down Expand Up @@ -143,7 +144,7 @@ extension Analyze {
@Dependency(\.fileManager) var fileManager
let checkoutDir = fileManager.checkoutsDirectory()
Current.logger().info("Checkout directory: \(checkoutDir)")
if !Current.fileManager.fileExists(atPath: checkoutDir) {
if !fileManager.fileExists(atPath: checkoutDir) {
try await createCheckoutsDirectory(client: client, path: checkoutDir)
}

Expand Down Expand Up @@ -236,9 +237,10 @@ extension Analyze {
path: String) async throws {
Current.logger().info("Creating checkouts directory at path: \(path)")
do {
try Current.fileManager.createDirectory(atPath: path,
withIntermediateDirectories: false,
attributes: nil)
@Dependency(\.fileManager) var fileManager
try fileManager.createDirectory(atPath: path,
withIntermediateDirectories: false,
attributes: nil)
} catch {
let error = AppError.genericError(nil, "Failed to create checkouts directory: \(error.localizedDescription)")
Current.logger().report(error: error)
Expand Down Expand Up @@ -266,11 +268,12 @@ extension Analyze {
/// - url: url to fetch from
/// - Throws: Shell errors
static func fetch(cacheDir: String, branch: String, url: String) async throws {
@Dependency(\.fileManager) var fileManager
Current.logger().info("pulling \(url) in \(cacheDir)")
// clean up stray lock files that might have remained from aborted commands
for fileName in ["HEAD.lock", "index.lock"] {
let filePath = cacheDir + "/.git/\(fileName)"
if Current.fileManager.fileExists(atPath: filePath) {
if fileManager.fileExists(atPath: filePath) {
Current.logger().info("Removing stale \(fileName) at path: \(filePath)")
try await Current.shell.run(command: .removeFile(from: filePath))
}
Expand All @@ -295,7 +298,7 @@ extension Analyze {
}

do {
guard Current.fileManager.fileExists(atPath: cacheDir) else {
guard fileManager.fileExists(atPath: cacheDir) else {
try await clone(cacheDir: cacheDir, url: package.model.url)
return
}
Expand Down Expand Up @@ -529,7 +532,8 @@ extension Analyze {
/// - Throws: Shell errors or AppError.invalidRevision if there is no Package.swift file
/// - Returns: `Manifest` data
static func dumpPackage(at path: String) async throws -> Manifest {
guard Current.fileManager.fileExists(atPath: path + "/Package.swift") else {
@Dependency(\.fileManager) var fileManager
guard fileManager.fileExists(atPath: path + "/Package.swift") else {
// It's important to check for Package.swift - otherwise `dump-package` will go
// up the tree through parent directories to find one
throw AppError.invalidRevision(nil, "no Package.swift")
Expand Down
2 changes: 1 addition & 1 deletion Sources/App/Commands/ReAnalyzeVersions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ enum ReAnalyzeVersions {
try await dependencies.yield {
@Dependency(\.fileManager) var fileManager
guard let cacheDir = fileManager.cacheDirectoryPath(for: pkg.model) else { return }
if !Current.fileManager.fileExists(atPath: cacheDir) || refreshCheckouts {
if !fileManager.fileExists(atPath: cacheDir) || refreshCheckouts {
try await Analyze.refreshCheckout(package: pkg)
}

Expand Down
25 changes: 0 additions & 25 deletions Sources/App/Core/AppEnvironment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import FoundationNetworking


struct AppEnvironment: Sendable {
var fileManager: FileManager
var git: Git
var logger: @Sendable () -> Logger
var setLogger: @Sendable (Logger) -> Void
Expand All @@ -35,7 +34,6 @@ extension AppEnvironment {
nonisolated(unsafe) static var logger: Logger!

static let live = AppEnvironment(
fileManager: .live,
git: .live,
logger: { logger },
setLogger: { logger in Self.logger = logger },
Expand All @@ -44,29 +42,6 @@ extension AppEnvironment {
}


struct FileManager: Sendable {
var createDirectory: @Sendable (String, Bool, [FileAttributeKey : Any]?) throws -> Void
var fileExists: @Sendable (String) -> Bool
var removeItem: @Sendable (_ path: String) throws -> Void
var workingDirectory: @Sendable () -> String

// pass-through methods to preserve argument labels
func createDirectory(atPath path: String,
withIntermediateDirectories createIntermediates: Bool,
attributes: [FileAttributeKey : Any]?) throws {
try createDirectory(path, createIntermediates, attributes)
}
func fileExists(atPath path: String) -> Bool { fileExists(path) }
func removeItem(atPath path: String) throws { try removeItem(path) }

static let live: Self = .init(
createDirectory: { try Foundation.FileManager.default.createDirectory(atPath: $0, withIntermediateDirectories: $1, attributes: $2) },
fileExists: { Foundation.FileManager.default.fileExists(atPath: $0) },
removeItem: { try Foundation.FileManager.default.removeItem(atPath: $0) },
workingDirectory: { DirectoryConfiguration.detect().workingDirectory }
)
}


struct Git: Sendable {
var commitCount: @Sendable (String) async throws -> Int
Expand Down
11 changes: 10 additions & 1 deletion Sources/App/Core/Dependencies/FileManagerClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ struct FileManagerClient {
var checkoutsDirectory: @Sendable () -> String = { reportIssue("checkoutsDirectory"); return "SPI-checkouts" }
var contents: @Sendable (_ atPath: String) -> Data?
var contentsOfDirectory: @Sendable (_ atPath: String) throws -> [String]
var createDirectory: @Sendable (_ atPath: String, _ withIntermediateDirectories: Bool, _ attributes: [FileAttributeKey : Any]?) throws -> Void
var fileExists: @Sendable (_ atPath: String) -> Bool = { reportIssue("fileExists"); return Foundation.FileManager.default.fileExists(atPath: $0) }
var removeItem: @Sendable (_ atPath: String) throws -> Void
var workingDirectory: @Sendable () -> String = { reportIssue("workingDirectory"); return "" }
}


Expand All @@ -43,7 +47,11 @@ extension FileManagerClient: DependencyKey {
attributesOfItem: { try Foundation.FileManager.default.attributesOfItem(atPath: $0) },
checkoutsDirectory: { Environment.get("CHECKOUTS_DIR") ?? DirectoryConfiguration.detect().workingDirectory + "SPI-checkouts" },
contents: { Foundation.FileManager.default.contents(atPath: $0) },
contentsOfDirectory: { try Foundation.FileManager.default.contentsOfDirectory(atPath: $0) }
contentsOfDirectory: { try Foundation.FileManager.default.contentsOfDirectory(atPath: $0) },
createDirectory: { try Foundation.FileManager.default.createDirectory(atPath: $0, withIntermediateDirectories: $1, attributes: $2) },
fileExists: { Foundation.FileManager.default.fileExists(atPath: $0) },
removeItem: { try Foundation.FileManager.default.removeItem(atPath: $0) },
workingDirectory: { DirectoryConfiguration.detect().workingDirectory }
)
}
}
Expand All @@ -54,6 +62,7 @@ extension FileManagerClient: TestDependencyKey {
var mock = Self()
// Override the `unimplemented` default because it is a very common dependency.
mock.checkoutsDirectory = { "SPI-checkouts" }
mock.workingDirectory = { DirectoryConfiguration.detect().workingDirectory }
return mock
}
}
Expand Down
6 changes: 4 additions & 2 deletions Sources/App/Core/Emoji.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
// limitations under the License.

import Foundation

import Dependencies
import Vapor


Expand All @@ -33,8 +35,8 @@ struct EmojiStorage {
var regularExpression: NSRegularExpression?

init() {
let pathToEmojiFile = Current.fileManager.workingDirectory()
.appending("Resources/emoji.json")
@Dependency(\.fileManager) var fileManager
let pathToEmojiFile = fileManager.workingDirectory().appending("Resources/emoji.json")

lookup = [:]
regularExpression = nil
Expand Down
8 changes: 5 additions & 3 deletions Sources/App/Core/Extensions/Badge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import Vapor
import Foundation

import Dependencies
import Vapor


struct Badge: Content, Equatable {
var schemaVersion: Int
Expand Down Expand Up @@ -68,8 +70,8 @@ enum BadgeType: String, Codable {
extension Badge {

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

return try? String(contentsOfFile: pathToFile, encoding: .utf8)
}
Expand Down
9 changes: 6 additions & 3 deletions Sources/App/Core/PackageCollection+signing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,12 @@ extension SignedCollection {
return true
}

static let certsDir = URL(fileURLWithPath: Current.fileManager.workingDirectory())
.appendingPathComponent("Resources")
.appendingPathComponent("Certs")
static var certsDir: URL {
@Dependency(\.fileManager) var fileManager
return URL(fileURLWithPath: fileManager.workingDirectory())
.appendingPathComponent("Resources")
.appendingPathComponent("Certs")
}

static let signer = PackageCollectionSigning(
trustedRootCertsDir: certsDir,
Expand Down
6 changes: 4 additions & 2 deletions Sources/App/Core/PackageContributors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
// limitations under the License.

import Foundation

import Dependencies
import ShellOut
import Vapor

Expand Down Expand Up @@ -65,8 +67,8 @@ enum PackageContributors {

/// Gets the git history in a string log
private static func runShortlog(gitCacheDirectoryPath: String, packageID: UUID?) async throws -> String {

if Current.fileManager.fileExists(atPath: gitCacheDirectoryPath) == false {
@Dependency(\.fileManager) var fileManager
if fileManager.fileExists(atPath: gitCacheDirectoryPath) == false {
throw AppError.cacheDirectoryDoesNotExist(packageID, gitCacheDirectoryPath)
}

Expand Down
5 changes: 3 additions & 2 deletions Sources/App/Views/Blog/BlogActions+Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ enum BlogActions {
struct Model {

static var blogIndexYmlPath: String {
Current.fileManager.workingDirectory().appending("Resources/Blog/posts.yml")
@Dependency(\.fileManager) var fileManager
return fileManager.workingDirectory().appending("Resources/Blog/posts.yml")
}

struct PostSummary: Equatable {
Expand Down Expand Up @@ -80,7 +81,7 @@ extension BlogActions.Model.PostSummary {

var postMarkdown: String {
@Dependency(\.fileManager) var fileManager
let markdownPath = Current.fileManager.workingDirectory()
let markdownPath = fileManager.workingDirectory()
.appending("Resources/Blog/Posts/")
.appending(slug)
.appending(".md")
Expand Down
7 changes: 5 additions & 2 deletions Sources/App/Views/MarkdownPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
// limitations under the License.

import Foundation
import Plot

import Dependencies
import Ink
import Plot


class MarkdownPage: PublicPage {
Expand All @@ -28,7 +30,8 @@ class MarkdownPage: PublicPage {
let html: String?

init(path: String, _ markdownFilename: String) {
let pathToMarkdownFile = Current.fileManager.workingDirectory()
@Dependency(\.fileManager) var fileManager
let pathToMarkdownFile = fileManager.workingDirectory()
.appending("Resources/Markdown/")
.appending(markdownFilename)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ enum ReadyForSwift6Show {

func readyForSwift6Chart(kind: ChartKind, includeTotals: Bool = false) -> Node<HTML.BodyContext> {
@Dependency(\.fileManager) var fileManager
let plotDataPath = Current.fileManager.workingDirectory().appending("Resources/ChartData/\(kind.dataFile)")
let eventDataPath = Current.fileManager.workingDirectory().appending("Resources/ChartData/rfs6-events.json")
let plotDataPath = fileManager.workingDirectory()
.appending("Resources/ChartData/\(kind.dataFile)")
let eventDataPath = fileManager.workingDirectory()
.appending("Resources/ChartData/rfs6-events.json")
guard let plotData = fileManager.contents(atPath: plotDataPath)?.compactJson(),
let eventData = fileManager.contents(atPath: eventDataPath)?.compactJson()
else { return .p("Couldn’t load chart data.") }
Expand Down
21 changes: 8 additions & 13 deletions Tests/AppTests/AnalyzeErrorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ final class AnalyzeErrorTests: AppTestCase {
func test_analyze_refreshCheckout_failed() async throws {
try await withDependencies {
$0.environment.loadSPIManifest = { _ in nil }
$0.fileManager.fileExists = { @Sendable _ in true }
} operation: {
Current.shell.run = { @Sendable cmd, path in
switch cmd {
Expand All @@ -130,9 +131,7 @@ final class AnalyzeErrorTests: AppTestCase {
}

// MUT
try await Analyze.analyze(client: app.client,
database: app.db,
mode: .limit(10))
try await Analyze.analyze(client: app.client, database: app.db, mode: .limit(10))

// validate
try await defaultValidation()
Expand All @@ -147,6 +146,7 @@ final class AnalyzeErrorTests: AppTestCase {
func test_analyze_updateRepository_invalidPackageCachePath() async throws {
try await withDependencies {
$0.environment.loadSPIManifest = { _ in nil }
$0.fileManager.fileExists = { @Sendable _ in true }
} operation: {
// setup
let pkg = try await Package.find(badPackageID, on: app.db).unwrap()
Expand All @@ -157,9 +157,7 @@ final class AnalyzeErrorTests: AppTestCase {
try await pkg.save(on: app.db)

// MUT
try await Analyze.analyze(client: app.client,
database: app.db,
mode: .limit(10))
try await Analyze.analyze(client: app.client, database: app.db, mode: .limit(10))

// validate
try await defaultValidation()
Expand All @@ -174,6 +172,7 @@ final class AnalyzeErrorTests: AppTestCase {
func test_analyze_getPackageInfo_gitCheckout_error() async throws {
try await withDependencies {
$0.environment.loadSPIManifest = { _ in nil }
$0.fileManager.fileExists = { @Sendable _ in true }
} operation: {
// setup
Current.shell.run = { @Sendable cmd, path in
Expand All @@ -187,9 +186,7 @@ final class AnalyzeErrorTests: AppTestCase {
}

// MUT
try await Analyze.analyze(client: app.client,
database: app.db,
mode: .limit(10))
try await Analyze.analyze(client: app.client, database: app.db, mode: .limit(10))

// validate
try await defaultValidation()
Expand All @@ -204,15 +201,13 @@ final class AnalyzeErrorTests: AppTestCase {
func test_analyze_dumpPackage_missing_manifest() async throws {
try await withDependencies {
$0.environment.loadSPIManifest = { _ in nil }
} operation: {
// setup
Current.fileManager.fileExists = { @Sendable path in
$0.fileManager.fileExists = { @Sendable path in
if path.hasSuffix("github.com-foo-1/Package.swift") {
return false
}
return true
}
} operation: {
// MUT
try await Analyze.analyze(client: app.client,
database: app.db,
Expand Down
Loading
Loading