Skip to content

Add Glob support for dictionary includes #1477

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
61 changes: 50 additions & 11 deletions Sources/ProjectSpec/SpecFile.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Foundation
import JSONUtilities
import PathKit
import XcodeGenCore
import Yams

public struct SpecFile {
Expand All @@ -26,24 +27,62 @@ public struct SpecFile {
static let defaultEnable = true

init?(any: Any) {
if let string = any as? String {
path = Path(string)
relativePaths = Include.defaultRelativePaths
enable = Include.defaultEnable
if let path = any as? String {
self.init(
path: Path(path),
relativePaths: Include.defaultRelativePaths,
enable: Include.defaultEnable
)
} else if let dictionary = any as? JSONDictionary, let path = dictionary["path"] as? String {
self.path = Path(path)
relativePaths = Self.resolveBoolean(dictionary, key: "relativePaths") ?? Include.defaultRelativePaths
enable = Self.resolveBoolean(dictionary, key: "enable") ?? Include.defaultEnable
self.init(
path: Path(path),
dictionary: dictionary
)
} else {
return nil
}
}

private init(path: Path, relativePaths: Bool, enable: Bool) {
self.path = path
self.relativePaths = relativePaths
self.enable = enable
}

private init?(path: Path, dictionary: JSONDictionary) {
self.path = path
relativePaths = Self.resolveBoolean(dictionary, key: "relativePaths") ?? Include.defaultRelativePaths
enable = Self.resolveBoolean(dictionary, key: "enable") ?? Include.defaultEnable
}

private func replacingPath(with newPath: Path) -> Include {
Include(
path: newPath,
relativePaths: relativePaths,
enable: enable
)
}

private static func includes(from array: [Any], basePath: Path) -> [Include] {
array.flatMap { entry -> [Include] in
if let string = entry as? String, let include = Include(any: string) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading this right only includes defined as a dictionary will be glob enabled, whereas plain strings won't?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I wanted to keep the changes as isolated to my use case as possible.
But I can have a look at extending it if you want to

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

started working on this, but still ironing out some stuff

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand, I see you've added support for a single string include, but not arrays of strings (where this line is). In breaking 3.0 version, support for these simpler strings could probably be dropped in the future.

Also ideally the glob logic would only be in one place where currently it is split across parse(json:) and includes(from:,basePath:)

return [include]
} else if let dictionary = entry as? JSONDictionary, let path = dictionary["path"] as? String {
return Glob(pattern: (basePath + Path(path)).normalize().string)
.compactMap { Include(path: Path($0), dictionary: dictionary) }
} else {
return []
}
}
}

static func parse(json: Any?) -> [Include] {
static func parse(json: Any?, basePath: Path) -> [Include] {
if let array = json as? [Any] {
return array.compactMap(Include.init)
return includes(from: array, basePath: basePath)
} else if let object = json, let include = Include(any: object) {
return [include]
return Glob(pattern: (basePath + include.path.normalize()).string)
.compactMap { try? Path($0).relativePath(from: basePath) }
.compactMap { include.replacingPath(with: $0) }
} else {
return []
}
Expand Down Expand Up @@ -92,7 +131,7 @@ public struct SpecFile {

let jsonDictionary = try SpecFile.loadDictionary(path: path).expand(variables: variables)

let includes = Include.parse(json: jsonDictionary["include"])
let includes = Include.parse(json: jsonDictionary["include"], basePath: basePath)
let subSpecs: [SpecFile] = try includes
.filter(\.enable)
.map { include in
Expand Down
3 changes: 3 additions & 0 deletions Tests/Fixtures/glob_include/glob_include_sut.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include:
- path: "*/*.yml"
name: GlobImportDependent
2 changes: 2 additions & 0 deletions Tests/Fixtures/glob_include/glob_include_sut_string.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include: "*/*.yml"
name: GlobImportDependent
4 changes: 4 additions & 0 deletions Tests/Fixtures/glob_include/includes/a_yaml.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name: DuplicatedImportRoot
fileGroups:
- First
- Second
2 changes: 2 additions & 0 deletions Tests/Fixtures/glob_include/includes/b_yaml.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fileGroups:
- Third
22 changes: 22 additions & 0 deletions Tests/ProjectSpecTests/SpecLoadingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@ class SpecLoadingTests: XCTestCase {
}
}

func testSpecLoaderGlobImports() {
describe {
$0.it("includes files via glob") {
let path = fixturePath + "glob_include/glob_include_sut.yml"
let project = try loadSpec(path: path)

try expect(project.fileGroups) == ["First", "Second", "Third"]
}
}
}

func testSpecLoaderGlobImportsString() {
describe {
$0.it("includes files via glob") {
let path = fixturePath + "glob_include/glob_include_sut_string.yml"
let project = try loadSpec(path: path)

try expect(project.fileGroups) == ["First", "Second", "Third"]
}
}
}

func testSpecLoader() {
describe {
$0.it("merges includes") {
Expand Down