diff --git a/README.md b/README.md index c3df50a..e9d8cfb 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,6 @@ targets: [ Just point the `xclint` binary at your `.xcodeproj`. Check out its `-h` flag for more usage. -⚠️ `.xclint.yml` support is a WIP. It does not work correctly at the moment. - ``` # xclint /path/to/MyProject.xcodeproj ``` @@ -40,8 +38,14 @@ Just point the `xclint` binary at your `.xcodeproj`. Check out its `-h` flag for This will run a default set of rules. But, you can customize its behavior with a `.xclint.yml` file. The basic structure borrows a lot from [SwiftLint](https://github.com/realm/SwiftLint). ```yaml -embedded_build_settings: warning -disabled_rules: ["dont", "run", "these"] +# Some rules may not be appropriate for all projects. You must opt-in those. +opt_in_rules: + - embedded_build_setting # checks for build settings in the project file + - groups_sorted # checks that all group contents are alphabetically sorted + +# Other rules make sense for all projects by default. You must opt-out of those. +disabled_rules: + - build_files_ordered. # checks that the ordering of techincally-unordered collections Xcode writes out is preserved ``` ## Alternatives diff --git a/Sources/XCLinting/ConfigurationFile.swift b/Sources/XCLinting/ConfigurationFile.swift index ef689a6..d3d2c70 100644 --- a/Sources/XCLinting/ConfigurationFile.swift +++ b/Sources/XCLinting/ConfigurationFile.swift @@ -2,14 +2,17 @@ import Foundation public struct Configuration: Hashable { public var disabledRules: Set + public var optInRules: Set public var rules: [String: RuleConfiguration] public init( rules: [String: RuleConfiguration] = [:], - disabledRules: Set = Set() + disabledRules: Set = Set(), + optInRules: Set = Set() ) { self.rules = rules self.disabledRules = disabledRules + self.optInRules = optInRules } } @@ -44,9 +47,11 @@ extension Configuration: Decodable { /// Contains all the top-level well-defined keys used in a configuration file enum PredefinedKeys: String { case disabledRules = "disabled_rules" + case optInRules = "opt_in_rules" } case disabledRules + case optInRules case ruleIdentifier(String) init?(intValue: Int) { @@ -57,6 +62,8 @@ extension Configuration: Decodable { switch stringValue { case PredefinedKeys.disabledRules.rawValue: self = .disabledRules + case PredefinedKeys.optInRules.rawValue: + self = .optInRules default: self = .ruleIdentifier(stringValue) } @@ -66,6 +73,8 @@ extension Configuration: Decodable { switch self { case .disabledRules: PredefinedKeys.disabledRules.rawValue + case .optInRules: + PredefinedKeys.optInRules.rawValue case let .ruleIdentifier(value): value } @@ -80,6 +89,7 @@ extension Configuration: Decodable { let container = try decoder.container(keyedBy: CodingKeys.self) self.disabledRules = Set() + self.optInRules = Set() var decodedRules = [String: RuleConfiguration]() @@ -87,6 +97,8 @@ extension Configuration: Decodable { switch key { case .disabledRules: self.disabledRules = try container.decode(Set.self, forKey: key) + case .optInRules: + self.optInRules = try container.decode(Set.self, forKey: key) case let .ruleIdentifier(name): let ruleConfig = try container.decode(RuleConfiguration.self, forKey: key) @@ -105,6 +117,12 @@ extension Configuration: Decodable { throw XCLintError.unrecognizedRuleName(id) } } + + for id in optInRules { + if allIdentifiers.contains(id) == false { + throw XCLintError.unrecognizedRuleName(id) + } + } } } @@ -112,6 +130,8 @@ extension Configuration { public var enabledRules: Set { let defaultIdentifiers = XCLinter.defaultRuleIdentifiers - return defaultIdentifiers.subtracting(disabledRules) + return defaultIdentifiers + .union(optInRules) + .subtracting(disabledRules) } } diff --git a/Sources/XCLinting/XCLinter.swift b/Sources/XCLinting/XCLinter.swift index 1e96556..76c4cbb 100644 --- a/Sources/XCLinting/XCLinter.swift +++ b/Sources/XCLinting/XCLinter.swift @@ -66,7 +66,6 @@ extension XCLinter.Environment { extension XCLinter { public static let defaultRuleIdentifiers: Set = [ - "embedded_build_setting", "build_files_ordered" ] diff --git a/Tests/XCLintTests/ConfigurationFileTests.swift b/Tests/XCLintTests/ConfigurationFileTests.swift index 21a527d..bdd7f00 100644 --- a/Tests/XCLintTests/ConfigurationFileTests.swift +++ b/Tests/XCLintTests/ConfigurationFileTests.swift @@ -25,6 +25,20 @@ final class ConfigurationTests: XCTestCase { XCTAssertEqual(config, expected) } + func testReadOptInRules() throws { + let string = """ +{ + "opt_in_rules": ["a", "b", "c"] +} +""" + + let config = try JSONDecoder().decode(Configuration.self, from: Data(string.utf8)) + + let expected = Configuration(optInRules: Set(["a", "b", "c"])) + + XCTAssertEqual(config, expected) + } + func testReadRules() throws { let string = """ {