diff --git a/.gitignore b/.gitignore index 02c0875..5eaf1a8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /.build /Packages /*.xcodeproj +portable_iblinter.zip diff --git a/IBLinter.podspec b/IBLinter.podspec index 0fe0940..eada4fe 100644 --- a/IBLinter.podspec +++ b/IBLinter.podspec @@ -1,10 +1,10 @@ Pod::Spec.new do |s| s.name = 'IBLinter' - s.version = '0.3.0' + s.version = '0.4.0' s.summary = 'A linter tool for Interface Builder.' s.homepage = 'https://github.com/IBDecodable/IBLinter' s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { 'Yuta Saito' => 'kateinoigakukun@gmail.com' } - s.source = { :git => "https://github.com/IBDecodable/IBLinter.git", :tag => s.version } - s.preserve_paths = 'bin/*' + s.source = { :http => "#{s.homepage}/releases/download/#{s.version}/portable_iblinter.zip" } + s.preserve_paths = '*' end diff --git a/Makefile b/Makefile index 5080d2b..84691a2 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,31 @@ PREFIX?=/usr/local -TEMPORARY_FOLDER=/tmp/IBLinter.dst +SWIFT_LIB_FILES = .build/release/libIBLinter.dylib .build/release/*.swiftmodule +C_LIB_DIRS = .build/release/CYaml.build build: - swift build --disable-sandbox -c release -Xswiftc -static-stdlib + swift build --disable-sandbox -c release --static-swift-stdlib clean_build: - rm -rf bin - make build - mkdir bin - cp -f .build/release/iblinter bin/ rm -rf .build + make build + +portable_zip: build + rm -rf portable_iblinter + mkdir portable_iblinter + mkdir portable_iblinter/lib + mkdir portable_iblinter/bin + cp -f .build/release/main portable_iblinter/bin/iblinter + cp -rf $(C_LIB_DIRS) $(SWIFT_LIB_FILES) "portable_iblinter/lib" + cp -f LICENSE portable_iblinter + cd portable_iblinter + (cd portable_iblinter; zip -yr - "lib" "bin" "LICENSE") > "./portable_iblinter.zip" + rm -rf portable_iblinter install: build mkdir -p "$(PREFIX)/bin" + mkdir -p "$(PREFIX)/lib/iblinter" cp -f ".build/release/iblinter" "$(PREFIX)/bin/iblinter" + cp -rf $(C_LIB_DIRS) $(SWIFT_LIB_FILES) "$(PREFIX)/lib/iblinter" publish: clean_build brew update && brew bump-formula-pr --tag=$(shell git describe --tags) --revision=$(shell git rev-parse HEAD) iblinter diff --git a/Package.resolved b/Package.resolved index d9834c2..4f26528 100644 --- a/Package.resolved +++ b/Package.resolved @@ -19,6 +19,15 @@ "version": null } }, + { + "package": "Files", + "repositoryURL": "https://github.com/JohnSundell/Files.git", + "state": { + "branch": null, + "revision": "06f95bd1bf4f8d5f50bc6bb30b808c253acd4c88", + "version": "2.2.1" + } + }, { "package": "IBDecodable", "repositoryURL": "https://github.com/IBDecodable/IBDecodable.git", @@ -28,13 +37,22 @@ "version": null } }, + { + "package": "Marathon", + "repositoryURL": "https://github.com/JohnSundell/Marathon.git", + "state": { + "branch": null, + "revision": "959e82b044f7dbb2680c6e7cdaa80d70e9dfa4ca", + "version": "3.0.0" + } + }, { "package": "Nimble", "repositoryURL": "https://github.com/Quick/Nimble.git", "state": { "branch": null, - "revision": "8023e3980d91b470ad073d6da843b73f2eeb1844", - "version": "7.1.2" + "revision": "9c1379fdcd58c4f2278aea5e029394ba9a2b8f07", + "version": "7.1.3" } }, { @@ -51,8 +69,26 @@ "repositoryURL": "https://github.com/Quick/Quick.git", "state": { "branch": null, - "revision": "3e3023569c8d4c4a0d000f58db765df53041117f", - "version": "1.3.0" + "revision": "b060679e70d13c3c7dcd124201b5b1b34ce6f340", + "version": "1.3.1" + } + }, + { + "package": "Releases", + "repositoryURL": "https://github.com/JohnSundell/Releases.git", + "state": { + "branch": null, + "revision": "e74f0895855b56147cb96f2d45aba3ec37da64fb", + "version": "3.0.0" + } + }, + { + "package": "Require", + "repositoryURL": "https://github.com/JohnSundell/Require.git", + "state": { + "branch": null, + "revision": "7cfbd0d8a2dede0e01f6f0d8ab2c7acef1df112e", + "version": "2.0.1" } }, { @@ -96,8 +132,26 @@ "repositoryURL": "https://github.com/drmohundro/SWXMLHash.git", "state": { "branch": null, - "revision": "2211b35c2e0e8b08493f86ba52b26e530cabb751", - "version": "4.7.0" + "revision": "24698592b581aef2bdbfc86ce18695708038c624", + "version": "4.7.1" + } + }, + { + "package": "Unbox", + "repositoryURL": "https://github.com/JohnSundell/Unbox.git", + "state": { + "branch": null, + "revision": "17ad6aa1cc2f1efa27a0bbbdb66f79796708aa95", + "version": "2.5.0" + } + }, + { + "package": "Wrap", + "repositoryURL": "https://github.com/JohnSundell/Wrap.git", + "state": { + "branch": null, + "revision": "8085c925060b84a1fc0caef444c130da2c8154c3", + "version": "3.0.1" } }, { diff --git a/Package.swift b/Package.swift index 5e0545e..012e039 100644 --- a/Package.swift +++ b/Package.swift @@ -6,7 +6,8 @@ import PackageDescription let package = Package( name: "IBLinter", products: [ - .executable(name: "iblinter", targets: ["iblinter"]), + .executable(name: "main", targets: ["main"]), + .library(name: "IBLinter", type: .dynamic, targets: ["IBLinter"]), .library(name: "IBLinterKit", targets: ["IBLinterKit"]), ], dependencies: [ @@ -15,17 +16,21 @@ let package = Package( .package(url: "https://github.com/IBDecodable/IBDecodable.git", .branch("master")), .package(url: "https://github.com/Carthage/Commandant.git", .branch("master")), .package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.21.1"), - .package(url: "https://github.com/xcodeswift/xcproj.git", from: "4.3.0") + .package(url: "https://github.com/xcodeswift/xcproj.git", from: "4.3.0"), + .package(url: "https://github.com/JohnSundell/Marathon.git", from: "3.0.0") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( - name: "iblinter", + name: "main", + dependencies: ["IBLinter"]), + .target( + name: "IBLinter", dependencies: ["IBLinterKit"]), .target( name: "IBLinterKit", - dependencies: ["IBDecodable", "Commandant", "SourceKittenFramework", "xcproj"]), + dependencies: ["IBDecodable", "Commandant", "SourceKittenFramework", "xcproj", "MarathonCore"]), .testTarget(name: "IBLinterKitTest", dependencies: ["IBLinterKit"]) ] diff --git a/Sources/IBLinterKit/App.swift b/Sources/IBLinterKit/App.swift deleted file mode 100644 index 4a8d854..0000000 --- a/Sources/IBLinterKit/App.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// App.swift -// IBLinterKit -// -// Created by SaitoYuta on 2017/12/11. -// - -import IBDecodable -import Commandant - -public struct App { - - public init() {} - - public func main() { - let registry = CommandRegistry>() - registry.register(ValidateCommand.init()) - registry.register(HelpCommand.init(registry: registry)) - registry.register(VersionCommand.init()) - - registry.main(defaultVerb: ValidateCommand.init().verb) { (error) in - print(String.init(describing: error)) - } - } - -} diff --git a/Sources/IBLinterKit/Context.swift b/Sources/IBLinterKit/Context.swift index 153dedb..93ca079 100644 --- a/Sources/IBLinterKit/Context.swift +++ b/Sources/IBLinterKit/Context.swift @@ -8,4 +8,11 @@ public struct Context { let config: Config let workDirectory: String + let externalRules: [Rule.Type] + + public init(config: Config, workDirectory: String, externalRules: [Rule.Type]) { + self.config = config + self.workDirectory = workDirectory + self.externalRules = externalRules + } } diff --git a/Sources/IBLinterKit/Reporters/Reporter.swift b/Sources/IBLinterKit/Reporters/Reporter.swift index b9d309c..a505c41 100644 --- a/Sources/IBLinterKit/Reporters/Reporter.swift +++ b/Sources/IBLinterKit/Reporters/Reporter.swift @@ -5,15 +5,15 @@ // Created by SaitoYuta on 2017/12/11. // -protocol Reporter { +public protocol Reporter { static var identifier: String { get } static func generateReport(violations: [Violation]) -> String } -struct Reporters { +public struct Reporters { - static func reporter(from reporter: String) -> Reporter.Type { + public static func reporter(from reporter: String) -> Reporter.Type { switch reporter { case XcodeReporter.identifier: return XcodeReporter.self diff --git a/Sources/IBLinterKit/Rules/Rule.swift b/Sources/IBLinterKit/Rules/Rule.swift index 8e5f8b8..9bfcef8 100644 --- a/Sources/IBLinterKit/Rules/Rule.swift +++ b/Sources/IBLinterKit/Rules/Rule.swift @@ -16,7 +16,7 @@ public protocol Rule { public struct Rules { - static var allRules: [Rule.Type] { + static var allRules: [Rule.Type] = { return [ CustomClassNameRule.self, RelativeToMarginRule.self, @@ -27,7 +27,7 @@ public struct Rules { ImageResourcesRule.self, CustomModuleRule.self ] - } + }() static var defaultRules: [Rule.Type] { return [ @@ -37,11 +37,12 @@ public struct Rules { ] } - static func rules(_ context: Context) -> [Rule] { + public static func rules(_ context: Context) -> [Rule] { var identifiers = Set(defaultRules.map({ $0.identifier })) identifiers.subtract(context.config.disabledRules) identifiers.formUnion(context.config.enabledRules) + let rules = allRules + context.externalRules - return allRules.filter { identifiers.contains($0.identifier) }.map { $0.init(context: context) } + return rules.filter { identifiers.contains($0.identifier) }.map { $0.init(context: context) } } } diff --git a/Sources/IBLinterKit/Violation.swift b/Sources/IBLinterKit/Violation.swift index 61b7433..bd0e538 100644 --- a/Sources/IBLinterKit/Violation.swift +++ b/Sources/IBLinterKit/Violation.swift @@ -9,15 +9,15 @@ import IBDecodable import Foundation public struct Violation { - let pathString: String - let message: String - let level: Level + public let pathString: String + public let message: String + public let level: Level - enum Level: String, Comparable { + public enum Level: String, Comparable { case warning case error - static func < (lhs: Violation.Level, rhs: Violation.Level) -> Bool { + public static func < (lhs: Violation.Level, rhs: Violation.Level) -> Bool { return lhs == .warning && rhs == .error } } diff --git a/Sources/iblinter/Commands/EditCommand.swift b/Sources/iblinter/Commands/EditCommand.swift new file mode 100644 index 0000000..5320dd6 --- /dev/null +++ b/Sources/iblinter/Commands/EditCommand.swift @@ -0,0 +1,72 @@ +// +// EditCommand.swift +// IBLinterKit +// +// Created by SaitoYuta on 2018/05/27. +// + +import Result +import Foundation +import Commandant +import Files +import PathKit +import xcproj + +struct EditCommand: CommandProtocol { + + let verb: String = "edit" + let function: String = "Edit the IBLinterfile.swift" + + func run(_ options: NoOptions>) -> Result<(), CommandantError<()>> { + let ibLinterfile = Runtime.ibLinterfile.exists ? Runtime.ibLinterfile : try! createIBLinterfile() + try! Runtime.resolvePackages(ibLinterfile: ibLinterfile) + guard let libPath = Runtime.libPath() else { + print("Could not find a libIBLinter to link against at any of: \(Runtime.potentialFolders)") + return Result.failure(.commandError(())) + } + let arguments = CommandLine.arguments + let scriptManager = try! Runtime.getScriptManager() + let script = try! scriptManager.script(atPath: ibLinterfile.string, allowRemote: true) + let xcodeprojPath = try! script.setupForEdit(arguments: arguments) + + try! addLibPathToXcodeProj(xcodeprojPath: xcodeprojPath, libPath: libPath) + try! script.watch(arguments: arguments) + return .success(()) + } + + func addLibPathToXcodeProj(xcodeprojPath: String, libPath: Path) throws -> Void { + let xcodeProj = try XcodeProj(pathString: xcodeprojPath) + let sourceRoot = Path(xcodeprojPath).parent() + + let otherSwiftFlagsKey = "OTHER_SWIFT_FLAGS" + xcodeProj.pbxproj.objects.buildConfigurations.forEach { (key, configuration) in + configuration.buildSettings["LIBRARY_SEARCH_PATHS"] = ["$(inherited)", libPath.string] + guard let flags = configuration.buildSettings[otherSwiftFlagsKey] as? [String] else { + return + } + configuration.buildSettings[otherSwiftFlagsKey] = flags + ["-I \(libPath.string)", "-L \(libPath.string)"] + } + + let fileReference = try xcodeProj.pbxproj.objects.addFile( + at: libPath + Runtime.dylibName, toGroup: xcodeProj.pbxproj.rootGroup, + sourceRoot: sourceRoot + ) + guard let target = xcodeProj.pbxproj.objects.targets(named: "IBLinterfile").first?.object else { + return + } + guard let buildFile = xcodeProj.pbxproj.objects + .addBuildFile(toTarget: target, reference: fileReference.reference) else { return } + xcodeProj.pbxproj.objects.frameworksBuildPhases.forEach { key, value in + value.files.append(buildFile.reference) + } + + try xcodeProj.write(path: Path(xcodeprojPath)) + } + + func createIBLinterfile() throws -> Path { + let template = "import IBLinter \nlet linter = IBLinter()" + let data = template.data(using: .utf8)! + let pathString = try FileSystem().createFile(at: Runtime.ibLinterfile.string, contents: data).path + return Path(pathString) + } +} diff --git a/Sources/IBLinterKit/Commands/ValidateCommand.swift b/Sources/iblinter/Commands/ValidateCommand.swift similarity index 80% rename from Sources/IBLinterKit/Commands/ValidateCommand.swift rename to Sources/iblinter/Commands/ValidateCommand.swift index 42cb771..431ef27 100644 --- a/Sources/IBLinterKit/Commands/ValidateCommand.swift +++ b/Sources/iblinter/Commands/ValidateCommand.swift @@ -8,7 +8,9 @@ import Result import Foundation import IBDecodable +import IBLinterKit import Commandant +import PathKit struct ValidateCommand: CommandProtocol { typealias Options = ValidateOptions @@ -17,7 +19,31 @@ struct ValidateCommand: CommandProtocol { let verb: String = "lint" var function: String = "Print lint warnings and errors (default command)" + let externalRules: [Rule.Type] + + init(externalRules: [Rule.Type]) { + self.externalRules = externalRules + } + func run(_ options: ValidateCommand.Options) -> Result<(), ValidateCommand.ClientError> { + + let iblinterFilePath: Path? = { + if let iblinterFilePathString = options.iblinterFilePath { + let iblinterFilePath = Path(iblinterFilePathString) + guard iblinterFilePath.exists else { + fatalError("\(iblinterFilePath) not found") + } + return iblinterFilePath + } else { + let defaultFile = Path("./IBLinterfile.swift") + guard defaultFile.exists else { return nil } + return defaultFile + } + }() + if let iblinterFilePath = iblinterFilePath { + return IBLinterRunner(ibLinterfile: iblinterFilePath).run() + } + let workDirectory = options.path ?? FileManager.default.currentDirectoryPath guard FileManager.default.isDirectory(workDirectory) else { fatalError("\(workDirectory) is not directory.") } let config = (try? Config.load(from: workDirectory)) ?? Config.default @@ -36,7 +62,7 @@ struct ValidateCommand: CommandProtocol { } private func validate(workDirectory: String, config: Config) -> [Violation] { - let context = Context(config: config, workDirectory: workDirectory) + let context = Context(config: config, workDirectory: workDirectory, externalRules: externalRules) let rules = Rules.rules(context) return validateXib(workDirectory: workDirectory, rules: rules, config: config) + validateStoryboard(workDirectory: workDirectory, rules: rules, config: config) } @@ -101,10 +127,13 @@ struct ValidateOptions: OptionsProtocol { let path: String? let reporter: String? + let iblinterFilePath: String? - static func create(_ path: String?) -> (_ reporter: String?) -> ValidateOptions { + static func create(_ path: String?) -> (_ reporter: String?) -> (_ script: String?) -> ValidateOptions { return { reporter in - self.init(path: path, reporter: reporter) + return { script in + self.init(path: path, reporter: reporter, iblinterFilePath: script) + } } } @@ -112,5 +141,6 @@ struct ValidateOptions: OptionsProtocol { return create <*> mode <| Option(key: "path", defaultValue: nil, usage: "validate project root directory") <*> mode <| Option(key: "reporter", defaultValue: nil, usage: "the reporter used to log errors and warnings") + <*> mode <| Option(key: "script", defaultValue: nil, usage: "custom IBLinterfile.swift") } } diff --git a/Sources/IBLinterKit/Commands/VersionCommand.swift b/Sources/iblinter/Commands/VersionCommand.swift similarity index 77% rename from Sources/IBLinterKit/Commands/VersionCommand.swift rename to Sources/iblinter/Commands/VersionCommand.swift index 80a8c62..e8df107 100644 --- a/Sources/IBLinterKit/Commands/VersionCommand.swift +++ b/Sources/iblinter/Commands/VersionCommand.swift @@ -10,9 +10,9 @@ import Commandant struct VersionCommand: CommandProtocol { let verb = "version" - let function = "Display the current version of SwiftLint" + let function = "Display the current version of IBLinter" - let currentVersion: String = "0.3.0" + let currentVersion: String = "0.4.0" func run(_ options: NoOptions>) -> Result<(), CommandantError<()>> { print(currentVersion) diff --git a/Sources/iblinter/IBLinter.swift b/Sources/iblinter/IBLinter.swift new file mode 100644 index 0000000..b618b27 --- /dev/null +++ b/Sources/iblinter/IBLinter.swift @@ -0,0 +1,27 @@ +// +// IBLinter.swift +// IBLinterKit +// +// Created by SaitoYuta on 2017/12/11. +// + +import IBLinterKit +import Commandant + +public struct IBLinter { + + public init() {} + + public func run(externalRules: [Rule.Type] = []) { + let registry = CommandRegistry>() + registry.register(ValidateCommand(externalRules: externalRules)) + registry.register(EditCommand()) + registry.register(HelpCommand(registry: registry)) + registry.register(VersionCommand()) + + registry.main(defaultVerb: ValidateCommand(externalRules: externalRules).verb) { (error) in + print(String.init(describing: error)) + } + } + +} diff --git a/Sources/iblinter/Runner/Runner.swift b/Sources/iblinter/Runner/Runner.swift new file mode 100644 index 0000000..2207bfa --- /dev/null +++ b/Sources/iblinter/Runner/Runner.swift @@ -0,0 +1,81 @@ +// +// Runner.swift +// iblinter +// +// Created by SaitoYuta on 2018/05/22. +// + +import Result +import Foundation +import PathKit +import Files +import IBLinterKit + +class IBLinterRunner { + let ibLinterfile: Path + init(ibLinterfile: Path) { + self.ibLinterfile = ibLinterfile + } + + func run() -> Result<(), ValidateCommand.ClientError> { + + func which(_ command: String) -> Path { + let process = Process() + process.launchPath = "/bin/sh" + process.arguments = ["-l", "-c", "which \(command)"] + let pipe = Pipe() + process.standardOutput = pipe + process.launch() + process.waitUntilExit() + let outputData = pipe.fileHandleForReading.readDataToEndOfFile() + guard var pathString = String(data: outputData, encoding: .utf8) else { + exit(1) + } + if pathString.count > 0 { + pathString.removeLast() + } + return Path.init(pathString) + } + + guard let dylib = Runtime.libPath() else { + print("Could not find a libIBLinter to link against at any of: \(Runtime.potentialFolders)") + exit(1) + } + + let marathonPath = try! Runtime.resolvePackages(ibLinterfile: ibLinterfile) + let artifactPaths = [".build/debug", ".build/release"] + + var arguments = [ + "-L", dylib.string, + "-I", dylib.string, + "-lIBLinter" + ] + if let marathonLibPath = artifactPaths.map({ marathonPath + $0 }).first(where: { $0.exists }) { + arguments += [ + "-L", marathonLibPath.string, + "-I", marathonLibPath.string, + "-lMarathonDependencies", + ] + } + arguments += [ibLinterfile.string] + arguments += Array(CommandLine.arguments.dropFirst()) + let process = Process() + let swift = which("swift") + let inputPipe = Pipe() + FileHandle.standardInput.writeabilityHandler = { + inputPipe.fileHandleForWriting.write($0.availableData) + } + process.standardInput = inputPipe + + process.launchPath = swift.string + process.arguments = arguments + + process.launch() + process.waitUntilExit() + if process.terminationStatus == 0 { + return .success(()) + } else { + exit(process.terminationStatus) + } + } +} diff --git a/Sources/iblinter/Runner/Runtime.swift b/Sources/iblinter/Runner/Runtime.swift new file mode 100644 index 0000000..97d87f7 --- /dev/null +++ b/Sources/iblinter/Runner/Runtime.swift @@ -0,0 +1,73 @@ +// +// Runtime.swift +// AEXML +// +// Created by SaitoYuta on 2018/05/27. +// + +import Foundation +import MarathonCore +import PathKit +import Files + +public struct Runtime { + + public static let potentialFolders = [ + Path.current + "/Pods/IBLinter/lib", + Path("/usr/local/lib/iblinter") + ] + + public static let ibLinterfile = Path("./IBLinterfile.swift") + static let tmpFolder = ".iblinter-tmp" + static let dylibName = "libIBLinter.dylib" + + public static func libPath() -> Path? { + guard let libPath = potentialFolders.first(where: { ($0 + dylibName).exists }) else { + return nil + } + return libPath + } + + public static func dylibPath() -> Path? { + return libPath().map { $0 + dylibName } + } + + @discardableResult + public static func resolvePackages(ibLinterfile: Path) throws -> Path { + let scriptManager = try getScriptManager(tmpFolder: tmpFolder) + let importExternalDeps = try ibLinterfile.read().components(separatedBy: .newlines) + .filter { $0.hasPrefix("import") && $0.contains("package: ") } + let tmpFileName = "_iblinter_imports.swift" + try Folder(path: tmpFolder).createFileIfNeeded(withName: tmpFileName) + let tmpFile = try File(path: "\(tmpFolder)/\(tmpFileName)") + try tmpFile.write(string: importExternalDeps.joined(separator: "\n")) + + let script = try scriptManager.script(atPath: tmpFile.path, allowRemote: true) + try script.build() + + return Path(script.folder.path) + } + + static func getScriptManager(tmpFolder: String = tmpFolder) throws -> ScriptManager { + let folder = tmpFolder + let printFunction = { print($0) } + let vPrintFunction = { (messageExpression: () -> String) in } + + let printer = Printer( + outputFunction: printFunction, + progressFunction: vPrintFunction, + verboseFunction: vPrintFunction + ) + let fileSystem = FileSystem() + + let rootFolder = try fileSystem.createFolderIfNeeded(at: folder) + let packageFolder = try rootFolder.createSubfolderIfNeeded(withName: "Packages") + let scriptFolder = try rootFolder.createSubfolderIfNeeded(withName: "Scripts") + + let packageManager = try PackageManager(folder: packageFolder, printer: printer) + let config = ScriptManager.Config(prefix: "package: ", file: "Dangerplugins") + + return try ScriptManager(folder: scriptFolder, packageManager: packageManager, printer: printer, config: config) + } + +} diff --git a/Sources/iblinter/main.swift b/Sources/iblinter/main.swift deleted file mode 100644 index afcac49..0000000 --- a/Sources/iblinter/main.swift +++ /dev/null @@ -1,4 +0,0 @@ -import IBLinterKit - -let app = App.init() -app.main() diff --git a/Sources/main/main.swift b/Sources/main/main.swift new file mode 100644 index 0000000..1d4eaf8 --- /dev/null +++ b/Sources/main/main.swift @@ -0,0 +1,4 @@ +import IBLinter + +let app = IBLinter() +app.run() diff --git a/Tests/IBLinterKitTest/RuleTest.swift b/Tests/IBLinterKitTest/RuleTest.swift index 7c54c73..bc664b3 100644 --- a/Tests/IBLinterKitTest/RuleTest.swift +++ b/Tests/IBLinterKitTest/RuleTest.swift @@ -5,7 +5,7 @@ import XCTest class RuleTest: XCTestCase { func context(from config: Config) -> Context { - return Context.init(config: config, workDirectory: FileManager.default.currentDirectoryPath) + return Context.init(config: config, workDirectory: FileManager.default.currentDirectoryPath, externalRules: []) } func testRelativeToMargin() {