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

Add --cache-path option #187

Open
wants to merge 3 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
9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
"version": "1.0.3"
}
},
{
"package": "swift-crypto",
"repositoryURL": "https://github.com/apple/swift-crypto.git",
"state": {
"branch": null,
"revision": "ddb07e896a2a8af79512543b1c7eb9797f8898a5",
"version": "1.1.7"
}
},
{
"package": "SWXMLHash",
"repositoryURL": "https://github.com/drmohundro/SWXMLHash.git",
Expand Down
5 changes: 5 additions & 0 deletions Sources/IBLinterFrontend/Commands/ValidateCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ struct ValidateCommand: ParsableCommand {
var reporter: String?
@Option(name: .long, help: "the path to IBLint's configuration file", completion: .file())
var configurationFile: String?
@Option(name: .long, help: "the directory of the cache used when linting", completion: .directory)
var cachePath: String?
@Argument(help: "included files/paths to lint. This is ignored if you specified included paths in your yml configuration file.",
completion: .file())
var included: [String] = []
Expand All @@ -38,6 +40,9 @@ struct ValidateCommand: ParsableCommand {
if config.included.isEmpty {
config.included = included
}
if let cachePath = cachePath {
config.cachePath = cachePath
}
let validator = Validator()
let violations = validator.validate(workDirectory: workDirectory, config: config)

Expand Down
6 changes: 5 additions & 1 deletion Sources/IBLinterKit/Config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public struct Config: Codable {
public let reporter: String
public let disableWhileBuildingForIB: Bool
public let ignoreCache: Bool
public var cachePath: String?

enum CodingKeys: String, CodingKey {
case disabledRules = "disabled_rules"
Expand All @@ -35,6 +36,7 @@ public struct Config: Codable {
case reporter = "reporter"
case disableWhileBuildingForIB = "disable_while_building_for_ib"
case ignoreCache = "ignore_cache"
case cachePath = "cache_path"
}

public static let fileName = ".iblinter.yml"
Expand All @@ -53,6 +55,7 @@ public struct Config: Codable {
reporter = "xcode"
disableWhileBuildingForIB = true
ignoreCache = false
cachePath = nil
}

init(disabledRules: [String] = [], enabledRules: [String] = [],
Expand All @@ -63,7 +66,7 @@ public struct Config: Codable {
useTraitCollectionsRule: UseTraitCollectionsConfig? = nil,
hidesBottomBarRule: HidesBottomBarConfig? = nil,
reporter: String = "xcode", disableWhileBuildingForIB: Bool = true,
ignoreCache: Bool = false) {
ignoreCache: Bool = false, cachePath: String? = nil) {
self.disabledRules = disabledRules
self.enabledRules = enabledRules
self.excluded = excluded
Expand All @@ -76,6 +79,7 @@ public struct Config: Codable {
self.reporter = reporter
self.disableWhileBuildingForIB = disableWhileBuildingForIB
self.ignoreCache = ignoreCache
self.cachePath = cachePath
}

public init(from decoder: Decoder) throws {
Expand Down
20 changes: 9 additions & 11 deletions Sources/IBLinterKit/Utils/Glob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,15 @@ public final class Glob {
let patterns = expandRecursiveStars(pattern: pattern)
var results: [String] = []

for pattern in patterns {
if executeGlob(pattern: pattern, gt: &gt) {
#if os(Linux)
let matchCount = Int(gt.gl_pathc)
#else
let matchCount = Int(gt.gl_matchc)
#endif
for i in 0..<matchCount {
let path = String.init(cString: gt.gl_pathv[i]!)
results.append(path)
}
for pattern in patterns where executeGlob(pattern: pattern, gt: &gt) {
#if os(Linux)
let matchCount = Int(gt.gl_pathc)
#else
let matchCount = Int(gt.gl_matchc)
#endif
for i in 0..<matchCount {
let path = String.init(cString: gt.gl_pathv[i]!)
results.append(path)
}
}
return Set(results.map(URL.init(fileURLWithPath: )))
Expand Down
35 changes: 24 additions & 11 deletions Sources/IBLinterKit/Utils/LintCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ struct LintEmptyCache: LintCache {
}

protocol CacheFileManager {
var cacheDir: URL { get }
var defaultCacheDir: URL { get }
func modificationDate(for path: String) -> Date?
func createCacheDirectory() throws
func createDirectory(at path: URL) throws
}

extension FileManager: CacheFileManager {
var cacheDir: URL {
var defaultCacheDir: URL {
return urls(for: .cachesDirectory, in: .userDomainMask)[0]
.appendingPathComponent("IBLinter/\(Version.current.value)")
}
Expand All @@ -37,20 +37,23 @@ extension FileManager: CacheFileManager {
return (try? attributesOfItem(atPath: path))?[.modificationDate] as? Date
}

func createCacheDirectory() throws {
try createDirectory(at: cacheDir, withIntermediateDirectories: true, attributes: nil)
func createDirectory(at path: URL) throws {
try createDirectory(at: path, withIntermediateDirectories: true, attributes: nil)
}
}

public class LintDiskCache: LintCache {
var content: LintCacheContent
let fileManager: CacheFileManager
let configHashKey: String
let cacheDir: URL

fileprivate init(content: LintCacheContent, fileManager: CacheFileManager, configHashKey: String) {
fileprivate init(content: LintCacheContent, fileManager: CacheFileManager,
configHashKey: String, cacheDir: URL) {
self.content = content
self.fileManager = fileManager
self.configHashKey = configHashKey
self.cacheDir = cacheDir
}

public func insertCache(for fileURL: URL, violations: [Violation]) {
Expand All @@ -71,18 +74,28 @@ public class LintDiskCache: LintCache {

extension LintDiskCache {

static func deriveCacheDir(from config: Config, fileManager: CacheFileManager) -> URL {
return config.cachePath.flatMap { URL(fileURLWithPath: $0) } ?? fileManager.defaultCacheDir
}

static func new(with fileManager: CacheFileManager, config: Config) throws -> LintCache {
let emptyContent = LintCacheContent(entries: [:])
let hashKey = try cacheHashKey(for: config)
return LintDiskCache(content: emptyContent, fileManager: fileManager, configHashKey: hashKey)
return LintDiskCache(content: emptyContent, fileManager: fileManager, configHashKey: hashKey,
cacheDir: Self.deriveCacheDir(from: config, fileManager: fileManager))
}

static func load(with fileManager: CacheFileManager, config: Config) throws -> LintCache {
let hashKey = try cacheHashKey(for: config)
let cacheFilePath = fileManager.cacheDir.appendingPathComponent(hashKey)
let cacheDir = Self.deriveCacheDir(from: config, fileManager: fileManager)
let cacheFilePath = cacheDir.appendingPathComponent(hashKey)
let cacheFileContent = try Data(contentsOf: cacheFilePath)
let content = try JSONDecoder().decode(LintCacheContent.self, from: cacheFileContent)
return LintDiskCache(content: content, fileManager: fileManager, configHashKey: hashKey)
return LintDiskCache(
content: content, fileManager: fileManager,
configHashKey: hashKey,
cacheDir: cacheDir
)
}

private static func cacheHashKey(for config: Config) throws -> String {
Expand All @@ -94,9 +107,9 @@ extension LintDiskCache {
}

public func save() throws {
let cacheFilePath = fileManager.cacheDir.appendingPathComponent(configHashKey)
let cacheFilePath = cacheDir.appendingPathComponent(configHashKey)
let contentData = try JSONEncoder().encode(content)
try fileManager.createCacheDirectory()
try fileManager.createDirectory(at: cacheDir)
try contentData.write(to: cacheFilePath)
}
}
Expand Down
57 changes: 46 additions & 11 deletions Tests/IBLinterKitTest/Utils/LintCacheTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,44 @@
import XCTest

class LintCacheTests: XCTestCase {
class MockFileManager: CacheFileManager {
let fixture = Fixture()
lazy var defaultCacheDir = makeTemporalyDirectory()
func modificationDate(for path: String) -> Date? {
return Date(timeIntervalSinceReferenceDate: 0.0)
}

func testLoadDiskCache() throws {
class MockFileManager: CacheFileManager {
let fixture = Fixture()
var cacheDir: URL {
return URL(fileURLWithPath: NSTemporaryDirectory())
}
func modificationDate(for path: String) -> Date? {
return Date(timeIntervalSinceReferenceDate: 0.0)
}

func createCacheDirectory() throws {}
func createDirectory(at path: URL) throws {
try FileManager.default.createDirectory(at: path)
}
}

func testCacheDirOption() throws {
let tmpDir = makeTemporalyDirectory()
let overrideCacheDir = tmpDir.appendingPathComponent("override")
let config = Config(cachePath: overrideCacheDir.path)
let fileManager = MockFileManager()
fileManager.defaultCacheDir = tmpDir.appendingPathComponent("default")
let cacheDir = LintDiskCache.deriveCacheDir(from: config, fileManager: fileManager)
XCTAssertEqual(cacheDir, overrideCacheDir)

let cache = try LintDiskCache.new(with: fileManager, config: config)
XCTAssertFalse(
FileManager.default.fileExists(atPath: fileManager.defaultCacheDir.path)
)
XCTAssertFalse(
FileManager.default.fileExists(atPath: overrideCacheDir.path)
)
try cache.save()
XCTAssertFalse(
FileManager.default.fileExists(atPath: fileManager.defaultCacheDir.path)
)
XCTAssertTrue(
FileManager.default.fileExists(atPath: overrideCacheDir.path)
)
}

func testLoadDiskCache() throws {
let config = Config()
let mockFileManager = MockFileManager()
let cache = try LintDiskCache.new(with: mockFileManager, config: config)
Expand All @@ -32,3 +57,13 @@ class LintCacheTests: XCTestCase {
XCTAssertEqual(restoredViolations?[0].message, "Warning message")
}
}

func makeTemporalyDirectory() -> URL {
let tempdir = URL(fileURLWithPath: NSTemporaryDirectory())
let templatePath = tempdir.appendingPathComponent("iblinter.XXXXXX")
var template = [UInt8](templatePath.path.utf8).map({ Int8($0) }) + [Int8(0)]
if mkdtemp(&template) == nil {
fatalError("Failed to create temp directory")
}
return URL(fileURLWithPath: String(cString: template))
}