Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Ignore special folder contents #2628

Merged
merged 5 commits into from
Nov 1, 2022
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
24 changes: 20 additions & 4 deletions Sources/ApolloCodegenLib/ApolloCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,14 @@ public class ApolloCodegen {
)
}

private static func createSchema(
static func createSchema(
_ config: ConfigurationContext,
_ frontend: GraphQLJSFrontend
) throws -> GraphQLSchema {
let matches = try Glob(config.input.schemaSearchPaths, relativeTo: config.rootURL).match()

let matches = try match(
searchPaths: config.input.schemaSearchPaths,
relativeTo: config.rootURL)

guard !matches.isEmpty else {
throw Error.cannotLoadSchema
Expand All @@ -197,12 +200,15 @@ public class ApolloCodegen {
return try frontend.loadSchema(from: sources)
}

private static func createOperationsDocument(
static func createOperationsDocument(
_ config: ConfigurationContext,
_ frontend: GraphQLJSFrontend,
_ experimentalFeatures: ApolloCodegenConfiguration.ExperimentalFeatures
) throws -> GraphQLDocument {
let matches = try Glob(config.input.operationSearchPaths, relativeTo: config.rootURL).match()

let matches = try match(
searchPaths: config.input.operationSearchPaths,
relativeTo: config.rootURL)

guard !matches.isEmpty else {
throw Error.cannotLoadOperations
Expand All @@ -217,6 +223,16 @@ public class ApolloCodegen {
return try frontend.mergeDocuments(documents)
}

static func match(searchPaths: [String], relativeTo relativeURL: URL?) throws -> OrderedSet<String> {
let excludedDirectories = [
".build",
".swiftpm",
".Pods"]

return try Glob(searchPaths, relativeTo: relativeURL)
.match(excludingDirectories: excludedDirectories)
}

/// Generates Swift files for the compiled schema, ir and configured output structure.
static func generateFiles(
compilationResult: CompilationResult,
Expand Down
33 changes: 23 additions & 10 deletions Sources/ApolloCodegenLib/Glob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ public struct Glob {
/// Executes the pattern match on the underlying file system.
///
/// - Returns: A set of matched file paths.
func match() throws -> OrderedSet<String> {
let expandedPatterns = try expand(self.patterns)
func match(excludingDirectories excluded: [String]? = nil) throws -> OrderedSet<String> {
let expandedPatterns = try expand(self.patterns, excludingDirectories: excluded)

var includeMatches: [String] = []
var excludeMatches: [String] = []
Expand All @@ -91,7 +91,10 @@ public struct Glob {
}

/// Separates a comma-delimited string into paths, expanding any globstars and removes duplicates.
private func expand(_ patterns: [String]) throws -> OrderedSet<String> {
private func expand(
_ patterns: [String],
excludingDirectories excluded: [String]?
) throws -> OrderedSet<String> {
var paths: OrderedSet<String> = []
for pattern in patterns {
if pattern.containsExclude && !pattern.isExclude {
Expand All @@ -101,14 +104,18 @@ public struct Glob {
throw MatchError.invalidExclude(path: pattern)
}

paths.formUnion(try expandGlobstar(pattern))
paths.formUnion(try expand(pattern, excludingDirectories: excluded))
}

return paths
}

/// Expands the globstar (`**`) to find all directory paths to search for the match pattern and removes duplicates.
private func expandGlobstar(_ pattern: String) throws -> OrderedSet<String> {
/// Expands `pattern` including any globstar character (`**`) to find all directory paths to
/// search for the match pattern and removes duplicates.
private func expand(
_ pattern: String,
excludingDirectories excluded: [String]?
) throws -> OrderedSet<String> {
guard pattern.includesGlobstar else {
return [URL(fileURLWithPath: pattern, relativeTo: rootURL).path]
}
Expand Down Expand Up @@ -136,7 +143,7 @@ public struct Glob {
}
}()

var directories: [String] = [searchURL.path] // include searching the globstar root directory
var directories: [URL] = [searchURL] // include searching the globstar root directory

do {
let resourceKeys: [URLResourceKey] = [.isDirectoryKey]
Expand All @@ -157,22 +164,28 @@ public struct Glob {

if let enumeratorError = enumeratorError { throw enumeratorError }

var excludedSet: Set<String> = []
if let excluded = excluded {
excludedSet = Set<String>(excluded)
}

for case (let url as URL) in enumerator {
guard
let resourceValues = try? url.resourceValues(forKeys: Set(resourceKeys)),
let isDirectory = resourceValues.isDirectory,
isDirectory == true
isDirectory == true,
excludedSet.intersection(url.pathComponents).isEmpty
else { continue }

directories.append(url.path)
directories.append(url)
}

} catch(let error) {
throw(error)
}

return OrderedSet<String>(directories.compactMap({ directory in
var path = URL(fileURLWithPath: directory).appendingPathComponent(lastPart).standardizedFileURL.path
var path = directory.appendingPathComponent(lastPart).standardizedFileURL.path
if isExclude {
path.insert("!", at: path.startIndex)
}
Expand Down
72 changes: 72 additions & 0 deletions Tests/ApolloCodegenTests/ApolloCodegenTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class ApolloCodegenTests: XCTestCase {
.appendingPathComponent("Codegen")
.appendingPathComponent(UUID().uuidString)
testFileManager = ApolloFileManager(base: FileManager.default)

try testFileManager.createDirectoryIfNeeded(atPath: directoryURL.path)
testFileManager.base.changeCurrentDirectoryPath(directoryURL.path)
}

override func tearDownWithError() throws {
Expand Down Expand Up @@ -2215,4 +2217,74 @@ class ApolloCodegenTests: XCTestCase {
.notTo(throwError())
}

// Special-folder Exclusion Tests

func test__match__givenFilesInSpecialExcludedPaths_shouldNotReturnExcludedPaths() throws {
// given
createFile(filename: "included.file")

createFile(filename: "excludedBuildFolder.file", inDirectory: ".build")
createFile(filename: "excludedBuildSubfolderOne.file", inDirectory: ".build/subfolder")
createFile(filename: "excludedBuildSubfolderTwo.file", inDirectory: ".build/subfolder/two")
createFile(filename: "excludedNestedOneBuildFolder.file", inDirectory: "nested/.build")
createFile(filename: "excludedNestedTwoBuildFolder.file", inDirectory: "nested/two/.build")

createFile(filename: "excludedSwiftpmFolder.file", inDirectory: ".swiftpm")
createFile(filename: "excludedSwiftpmSubfolderOne.file", inDirectory: ".swiftpm/subfolder")
createFile(filename: "excludedSwiftpmSubfolderTwo.file", inDirectory: ".swiftpm/subfolder/two")
createFile(filename: "excludedNestedOneSwiftpmFolder.file", inDirectory: "nested/.swiftpm")
createFile(filename: "excludedNestedTwoSwiftpmFolder.file", inDirectory: "nested/two/.swiftpm")

createFile(filename: "excludedPodsFolder.file", inDirectory: ".Pods")
createFile(filename: "excludedPodsSubfolderOne.file", inDirectory: ".Pods/subfolder")
createFile(filename: "excludedPodsSubfolderTwo.file", inDirectory: ".Pods/subfolder/two")
createFile(filename: "excludedNestedOnePodsFolder.file", inDirectory: "nested/.Pods")
createFile(filename: "excludedNestedTwoPodsFolder.file", inDirectory: "nested/two/.Pods")

// when
let matches = try ApolloCodegen.match(
searchPaths: ["\(directoryURL.path)/**/*.file"],
relativeTo: nil)

// then
expect(matches.count).to(equal(1))
expect(matches.contains(where: { $0.contains(".build") })).to(beFalse())
expect(matches.contains(where: { $0.contains(".swiftpm") })).to(beFalse())
expect(matches.contains(where: { $0.contains(".Pods") })).to(beFalse())
}

func test__match__givenFilesInSpecialExcludedPaths_usingRelativeDirectory_shouldNotReturnExcludedPaths() throws {
// given
createFile(filename: "included.file")

createFile(filename: "excludedBuildFolder.file", inDirectory: ".build")
createFile(filename: "excludedBuildSubfolderOne.file", inDirectory: ".build/subfolder")
createFile(filename: "excludedBuildSubfolderTwo.file", inDirectory: ".build/subfolder/two")
createFile(filename: "excludedNestedOneBuildFolder.file", inDirectory: "nested/.build")
createFile(filename: "excludedNestedTwoBuildFolder.file", inDirectory: "nested/two/.build")

createFile(filename: "excludedSwiftpmFolder.file", inDirectory: ".swiftpm")
createFile(filename: "excludedSwiftpmSubfolderOne.file", inDirectory: ".swiftpm/subfolder")
createFile(filename: "excludedSwiftpmSubfolderTwo.file", inDirectory: ".swiftpm/subfolder/two")
createFile(filename: "excludedNestedOneSwiftpmFolder.file", inDirectory: "nested/.swiftpm")
createFile(filename: "excludedNestedTwoSwiftpmFolder.file", inDirectory: "nested/two/.swiftpm")

createFile(filename: "excludedPodsFolder.file", inDirectory: ".Pods")
createFile(filename: "excludedPodsSubfolderOne.file", inDirectory: ".Pods/subfolder")
createFile(filename: "excludedPodsSubfolderTwo.file", inDirectory: ".Pods/subfolder/two")
createFile(filename: "excludedNestedOnePodsFolder.file", inDirectory: "nested/.Pods")
createFile(filename: "excludedNestedTwoPodsFolder.file", inDirectory: "nested/two/.Pods")

// when
let matches = try ApolloCodegen.match(
searchPaths: ["**/*.file"],
relativeTo: directoryURL)

// then
expect(matches.count).to(equal(1))
expect(matches.contains(where: { $0.contains(".build") })).to(beFalse())
expect(matches.contains(where: { $0.contains(".swiftpm") })).to(beFalse())
expect(matches.contains(where: { $0.contains(".Pods") })).to(beFalse())
}

}
Loading