mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
move repo scan to background
This commit is contained in:
parent
e02f0c555d
commit
56589b8a0e
1 changed files with 64 additions and 28 deletions
|
|
@ -11,6 +11,46 @@ extension Logger {
|
|||
)
|
||||
}
|
||||
|
||||
// MARK: - FileSystemScanner
|
||||
|
||||
/// Actor to handle file system operations off the main thread
|
||||
private actor FileSystemScanner {
|
||||
/// Scan directory contents
|
||||
func scanDirectory(at url: URL) throws -> [URL] {
|
||||
try FileManager.default.contentsOfDirectory(
|
||||
at: url,
|
||||
includingPropertiesForKeys: [.isDirectoryKey, .isHiddenKey],
|
||||
options: [.skipsSubdirectoryDescendants]
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if path is readable
|
||||
func isReadable(at path: String) -> Bool {
|
||||
FileManager.default.isReadableFile(atPath: path)
|
||||
}
|
||||
|
||||
/// Check if Git repository exists
|
||||
func isGitRepository(at path: String) -> Bool {
|
||||
let gitPath = URL(fileURLWithPath: path).appendingPathComponent(".git").path
|
||||
return FileManager.default.fileExists(atPath: gitPath)
|
||||
}
|
||||
|
||||
/// Get modification date for a file
|
||||
func getModificationDate(at path: String) throws -> Date {
|
||||
let attributes = try FileManager.default.attributesOfItem(atPath: path)
|
||||
return attributes[.modificationDate] as? Date ?? Date.distantPast
|
||||
}
|
||||
|
||||
/// Get directory and hidden status for URL
|
||||
func getDirectoryStatus(for url: URL) throws -> (isDirectory: Bool, isHidden: Bool) {
|
||||
let resourceValues = try url.resourceValues(forKeys: [.isDirectoryKey, .isHiddenKey])
|
||||
return (
|
||||
isDirectory: resourceValues.isDirectory ?? false,
|
||||
isHidden: resourceValues.isHidden ?? false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Service for discovering Git repositories in a specified directory
|
||||
///
|
||||
/// Provides functionality to scan a base directory for Git repositories and
|
||||
|
|
@ -36,6 +76,9 @@ public final class RepositoryDiscoveryService {
|
|||
/// Maximum depth to search for repositories (prevents infinite recursion)
|
||||
private let maxSearchDepth = 3
|
||||
|
||||
/// File system scanner actor for background operations
|
||||
private let fileScanner = FileSystemScanner()
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
public init() {}
|
||||
|
|
@ -85,7 +128,10 @@ public final class RepositoryDiscoveryService {
|
|||
|
||||
/// Perform the actual discovery work
|
||||
private func performDiscovery(in basePath: String) async -> [DiscoveredRepository] {
|
||||
let allRepositories = await scanDirectory(basePath, depth: 0)
|
||||
// Move the heavy file system work to a background actor
|
||||
let allRepositories = await Task.detached(priority: .userInitiated) {
|
||||
await self.scanDirectory(basePath, depth: 0)
|
||||
}.value
|
||||
|
||||
// Sort by folder name for consistent display
|
||||
return allRepositories.sorted { $0.folderName < $1.folderName }
|
||||
|
|
@ -103,37 +149,32 @@ public final class RepositoryDiscoveryService {
|
|||
}
|
||||
|
||||
do {
|
||||
let fileManager = FileManager.default
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
// Check if directory is accessible
|
||||
guard fileManager.isReadableFile(atPath: path) else {
|
||||
// Check if directory is accessible using actor
|
||||
guard await fileScanner.isReadable(at: path) else {
|
||||
Logger.repositoryDiscovery.debug("Directory not readable: \(path)")
|
||||
return []
|
||||
}
|
||||
|
||||
// Get directory contents
|
||||
let contents = try fileManager.contentsOfDirectory(
|
||||
at: url,
|
||||
includingPropertiesForKeys: [.isDirectoryKey, .isHiddenKey],
|
||||
options: [.skipsSubdirectoryDescendants]
|
||||
)
|
||||
// Get directory contents using actor
|
||||
let contents = try await fileScanner.scanDirectory(at: url)
|
||||
|
||||
var repositories: [DiscoveredRepository] = []
|
||||
|
||||
for itemURL in contents {
|
||||
let resourceValues = try itemURL.resourceValues(forKeys: [.isDirectoryKey, .isHiddenKey])
|
||||
let (isDirectory, isHidden) = try await fileScanner.getDirectoryStatus(for: itemURL)
|
||||
|
||||
// Skip files and hidden directories (except .git)
|
||||
guard resourceValues.isDirectory == true else { continue }
|
||||
if resourceValues.isHidden == true && itemURL.lastPathComponent != ".git" {
|
||||
guard isDirectory else { continue }
|
||||
if isHidden && itemURL.lastPathComponent != ".git" {
|
||||
continue
|
||||
}
|
||||
|
||||
let itemPath = itemURL.path
|
||||
|
||||
// Check if this directory is a Git repository
|
||||
if isGitRepository(at: itemPath) {
|
||||
// Check if this directory is a Git repository using actor
|
||||
if await fileScanner.isGitRepository(at: itemPath) {
|
||||
let repository = await createDiscoveredRepository(at: itemPath)
|
||||
repositories.append(repository)
|
||||
} else {
|
||||
|
|
@ -150,24 +191,20 @@ public final class RepositoryDiscoveryService {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if a directory is a Git repository
|
||||
private func isGitRepository(at path: String) -> Bool {
|
||||
let gitPath = URL(fileURLWithPath: path).appendingPathComponent(".git").path
|
||||
return FileManager.default.fileExists(atPath: gitPath)
|
||||
}
|
||||
|
||||
/// Create a DiscoveredRepository from a path
|
||||
private func createDiscoveredRepository(at path: String) async -> DiscoveredRepository {
|
||||
let url = URL(fileURLWithPath: path)
|
||||
let folderName = url.lastPathComponent
|
||||
|
||||
// Get last modified date
|
||||
let lastModified = getLastModifiedDate(at: path)
|
||||
let lastModified = await getLastModifiedDate(at: path)
|
||||
|
||||
// Get GitHub URL (this might be slow, so we do it in background)
|
||||
let githubURL = GitRepository.getGitHubURL(for: path)
|
||||
// Get GitHub URL in parallel (this might be slow)
|
||||
async let githubURL = Task.detached(priority: .background) {
|
||||
GitRepository.getGitHubURL(for: path)
|
||||
}.value
|
||||
|
||||
return DiscoveredRepository(
|
||||
return await DiscoveredRepository(
|
||||
path: path,
|
||||
folderName: folderName,
|
||||
lastModified: lastModified,
|
||||
|
|
@ -176,10 +213,9 @@ public final class RepositoryDiscoveryService {
|
|||
}
|
||||
|
||||
/// Get the last modified date of a repository
|
||||
private func getLastModifiedDate(at path: String) -> Date {
|
||||
private func getLastModifiedDate(at path: String) async -> Date {
|
||||
do {
|
||||
let attributes = try FileManager.default.attributesOfItem(atPath: path)
|
||||
return attributes[.modificationDate] as? Date ?? Date.distantPast
|
||||
return try await fileScanner.getModificationDate(at: path)
|
||||
} catch {
|
||||
Logger.repositoryDiscovery.debug("Could not get modification date for \(path): \(error)")
|
||||
return Date.distantPast
|
||||
|
|
|
|||
Loading…
Reference in a new issue