使用 Swift 递归搜索目录中文件的内容,同时支持 Glob 模式和正则表达式

2024-07-05 15:22:36 浏览数 (2)

前言

如果你新加入一个团队,想要快速的了解团队的领域和团队中拥有的代码库的详细信息。

如果新团队中的代码库在 GitHub / GitLab 中并且你不熟悉代码所有权模型的概念或格式。本篇文章以 GitHub 为例,你可以使用 Glob 模式将一个或多个文件链接到 GitHub 团队。

如果新团队中的代码库有一个 GitHub 的 CODEOWNERS 文件,可以反映拥有的每个文件或文件组。这是对了解整个框架有很大帮助,如果没有,可以尝试创建一个。如下:

  • /Tests/ @MyAwesomeOrg/cool-beans
  • /Modules/Account/Tests/* @MyAwesomeOrg/cool-beans
  • /Modules/Account/Settings/**/Views @MyAwesomeOrg/cool-beans

我曾经经历手动去查找团队拥有的文件中的文本出现的次数,比如固定模块的多次重复使用,这非常的耗费时间。

本篇文章讲帮助大家写一个小脚本来自动完成这项任务,给定一些文本片段和一个 GitHub 团队标签,它将在团队拥有的文件中找到该文本的所有出现次数。

项目设置

首先,要做的第一件事是创建一个可执行的 Swift Package:

代码语言:shell复制
mkdir find-code-owner && cd find-code-owner
swift package init --name FindCodeOwner --type executable

然后,将 ChimeHQGlobPattern Swift Package 添加为依赖项,以帮助确定包含查询文本的文件是否由提供的 GitHub 团队拥有:

代码语言:swift复制
// swift-tools-version: 5.10

import PackageDescription

let package = Package(
    name: "FindCodeOwner",
    platforms: [
        .macOS(.v13)
    ],
    dependencies: [
        .package(url: "https://github.com/ChimeHQ/GlobPattern.git", exact: "0.1.1")
    ],
    targets: [
        .executableTarget(
            name: "FindCodeOwner",
            dependencies: ["GlobPattern"],
            swiftSettings: [
                .enableUpcomingFeature("BareSlashRegexLiterals")
            ]
        ),
    ]
)

查找文件

假设我们的团队想要迁移一个名为 Quick 的依赖,我们想要找到所有我们拥有的导入该库的文件。

让我们在我们的可执行目标中编写一些代码来实现这一点:

代码语言:swift复制
import Foundation
import GlobPattern

struct OwnershipRule {
    let path: String
    let teams: [String]
}

func getRules(from codeOwnersFile: String, relativeTo repository: String) -> [OwnershipRule] {
    guard let content = try? String(contentsOfFile: codeOwnersFile) else {
        return []
    }
    
    return content
        .components(separatedBy: .newlines)
        .filter { $0.isEmpty || $0.hasPrefix("#") }
        .map { createRule(from: $0, relativeTo: repository) }
}

func createRule(from line: String, relativeTo repository: String) -> OwnershipRule {
    let elements = line.components(separatedBy: .whitespaces)
        .filter { !$0.isEmpty }

    let teams = elements
        .enumerated()
        .filter { $0 != 0 && $1.hasPrefix("@") }
        .map(.1)
    
    return OwnershipRule(path: repository   elements[0], teams: teams)
}

func getOwnersForFile(_ filePath: String, rules: [OwnershipRule]) -> [String] {
    rules
        .reversed()
        .first { rule in
            let globExpression = URL(string: rule.path)?.hasDirectoryPath == true ? rule.path   "*" : rule.path
            let matcher = try? Glob.Pattern(globExpression)
            return matcher?.match(filePath) == true
        }?
        .teams ?? []
}

// 1
let rootRepositoryDirectory = FileManager.default.currentDirectoryPath
let codeOwnersPath = rootRepositoryDirectory   "/.github/CODEOWNERS"

// 2
let allOwnershipRules = getRules(from: codeOwnersPath, relativeTo: rootRepositoryDirectory)

// 3
let matchingSearch = "import Quick"
let dirEnum = FileManager.default.enumerator(atPath: rootRepositoryDirectory)
var matchedFiles = [String]()
while let file = dirEnum?.nextObject() as? String {
    guard file.hasSuffix(".swift") else { continue }

    let fullPath = rootRepositoryDirectory   "/"   file
    if let contents = FileManager.default.contents(atPath: fullPath),
       let stringContents = String(data: contents, encoding: .utf8),
       stringContents.contains(matchingSearch) {

        matchedFiles.append(fullPath)
    }
}

// 4
let matchedFilesOnwedByTeam = matchedFiles
    .filter { fileContainingSearchQuery in
        getOwnersForFile(fileContainingSearchQuery, rules: allOwnershipRules).contains("@MyAwesomeOrg/cool-beans")
    }

// 5
print(matchedFilesOnwedByTeam)

上面这段代码的主要目的是从代码库中查找特定团队拥有的文件,并筛选出其中包含指定文本的文件。让我们逐步解释代码的意义、作用和可扩展性。

读取CODEOWNERS文件

通过 getRules(from: codeOwnersPath, relativeTo: rootRepositoryDirectory) 函数从 CODEOWNERS 文件中获取规则。

这些规则定义了哪些文件或目录由特定团队拥有。

解析规则

getRules(from: codeOwnersPath, relativeTo: rootRepositoryDirectory) 函数解析 CODEOWNERS 文件的内容,生成 OwnershipRule 结构体的数组。

每个 OwnershipRule 结构体包含文件路径和相应的团队。

搜索匹配的文件

脚本使用 FileManager 遍历当前代码库中的所有 .swift 文件。

对于每个文件,检查是否包含了匹配的文本(例如,import Quick)。

确定文件所有者

对于包含匹配文本的文件,使用 getOwnersForFile(_:_:) 函数确定其所有者。

getOwnersForFile(_:_:) 函数根据文件路径和规则数组,确定文件的拥有者团队。

输出结果

将文件所有者为 @MyAwesomeOrg/cool-beans 的匹配文件打印输出。

通过这段脚本可以帮助开发者快速找到特定团队拥有的文件,并检查其中是否包含特定的文本。它的可扩展性取决于 CODEOWNERS 文件的格式和内容,以及要搜索的文本类型。例如,可以扩展代码以支持更多类型的文本搜索,或者为不同的团队提供不同的匹配逻辑。此外,可以根据需要添加更多的文件过滤规则或其他自定义逻辑。

总结

最后我想到了一些更加实用的功能,抽时间给大家分享。在未来,可以考虑添加更多的文件过滤规则或支持其他类型的文本搜索,以增强功能。例如,可以添加对不同文件类型的支持,或者实现更复杂的团队匹配逻辑。另外,还可以考虑添加用户界面和更友好的输出方式,以提升用户体验。

我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!

0 人点赞