Skip to content

Commit 82a7f01

Browse files
authored
Honoring the original order from a CODEOWNERS entry (#37)
1 parent 346178d commit 82a7f01

File tree

9 files changed

+25
-18
lines changed

9 files changed

+25
-18
lines changed

Demo/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Sources/Demo/ @demo-devs
1+
Sources/Demo/ @demo-devs @baz-devs
22
Foo/ @foo-devs
33
Bar/ @bar-devs
44
**/*Enum.swift @team/enum-experts
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
@freestanding(expression)
2-
public macro codeOwners() -> Set<String>? = #externalMacro(module: "Macros", type: "CodeOwnersMacro")
2+
public macro codeOwners() -> [String]? = #externalMacro(module: "Macros", type: "CodeOwnersMacro")

Demo/Tests/DemoTests/GenericTest.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import CodeOwnersAPI
44

55
@Test
66
func ownersOfGenericStruct() {
7-
#expect(["demo-devs"] == codeOwnersOf(GenericStruct<Any>.self))
8-
#expect(["demo-devs"] == codeOwnersOf(GenericStruct(value: "aValue")))
7+
#expect(["demo-devs", "baz-devs"] == codeOwnersOf(GenericStruct<Any>.self))
8+
#expect(["demo-devs", "baz-devs"] == codeOwnersOf(GenericStruct(value: "aValue")))
99
}
1010

1111
@Test
1212
func ownersOfGenericClass() {
13-
#expect(["demo-devs"] == codeOwnersOf(GenericClass<Any>.self))
14-
#expect(["demo-devs"] == codeOwnersOf(GenericClass(value: "aValue")))
13+
#expect(["demo-devs", "baz-devs"] == codeOwnersOf(GenericClass<Any>.self))
14+
#expect(["demo-devs", "baz-devs"] == codeOwnersOf(GenericClass(value: "aValue")))
1515
}

Demo/Tests/DemoTests/SomeClassTest.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ import CodeOwnersAPI
44

55
@Test
66
func ownersOfSomeClass() {
7-
#expect(["demo-devs"] == codeOwnersOf(SomeClassImpl.self))
8-
#expect(["demo-devs"] == codeOwnersOf(SomeClassImpl()))
7+
#expect(["demo-devs", "baz-devs"] == codeOwnersOf(SomeClassImpl.self))
8+
#expect(["demo-devs", "baz-devs"] == codeOwnersOf(SomeClassImpl()))
99
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ Finally, import the macro module and use the `#codeowners` macro in your code:
111111

112112
```swift
113113
@freestanding(expression)
114-
public macro codeOwners() -> Set<String>? = #externalMacro(module: "MyMacros", type: "CodeOwnersMacro")
114+
public macro codeOwners() -> [String]? = #externalMacro(module: "MyMacros", type: "CodeOwnersMacro")
115115
```
116116

117117
# The CODEOWNERS file

Sources/CodeOwnersAPI/CodeOwnersAPI.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ private let lock = NSLock()
44
nonisolated(unsafe) private var ownersCache: [Substring: [Substring: CodeOwners]] = [:]
55
nonisolated(unsafe) private let nsClassNameRegEx = /(\w+)\.(\w+)/
66

7-
public typealias CodeOwners = Set<String>
7+
public typealias CodeOwners = [String] // order is important for attribution
88

99
public protocol CodeOwnersMappingProvider {
1010
static var codeOwners: [Substring: CodeOwners]? { get }

Sources/CodeOwnersResolver/CodeOwnersResolver.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ public struct CodeOwnersResolver {
55
fileprivate let root: URL
66
fileprivate let entries: CodeOwners
77

8-
public func codeOwnersOf(_ file: URL) -> Set<String>? {
8+
public func codeOwnersOf(_ file: URL) -> [String]? {
99
guard let relativePath = file.relativePathTo(root) else { return nil }
1010
guard let owners = entries.codeOwner(pattern: relativePath)?.owners else { return nil }
11-
return Set(owners.map(asLiteral))
11+
return owners.map(asLiteral)
1212
}
1313
}
1414

Sources/CodeOwnersTool/CodeOwnersTool.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ struct CodeOwnersTool: AsyncParsableCommand {
5757
renames: renames
5858
)
5959

60-
var mappings: [Substring: Set<String>] = [:]
60+
var mappings: [Substring: [String]] = [:]
6161

6262
try fm.walkFiles(at: sources) { sourceFile in
6363
if sourceFile.pathExtension != "swift" { return }
@@ -67,7 +67,7 @@ struct CodeOwnersTool: AsyncParsableCommand {
6767

6868
do {
6969
for typeName in try collectTypes(from: sourceFile) {
70-
mappings[typeName] = (mappings[typeName] ?? []).union(owners)
70+
mappings[typeName] = (mappings[typeName] ?? []) + owners
7171
}
7272

7373
} catch {
@@ -92,7 +92,7 @@ struct CodeOwnersTool: AsyncParsableCommand {
9292
return collector.rootTypes
9393
}
9494

95-
private func generateContent(_ mappings: [Substring: Set<String>]) -> String {
95+
private func generateContent(_ mappings: [Substring: [String]]) -> String {
9696
if mappings.isEmpty { return "" }
9797

9898
var content = """
@@ -103,7 +103,7 @@ struct CodeOwnersTool: AsyncParsableCommand {
103103
104104
"""
105105
for typeName in mappings.keys.sorted() {
106-
let owners = mappings[typeName]!.sorted().map { "\"\($0)\"" }.joined(separator: ", ")
106+
let owners = mappings[typeName]!.distinct().map { "\"\($0)\"" }.joined(separator: ", ")
107107

108108
content += " \"\(typeName)\": [\(owners)],\n"
109109
}
@@ -121,3 +121,10 @@ private func asRenameRule(regex: String, replace: String) -> RenameRule {
121121
if let rule = RenameRule(argument: argument) { return rule }
122122
fatalError("Rename rule should be in the <regex>=<replacement> format: \(argument)")
123123
}
124+
125+
private extension Sequence where Iterator.Element: Hashable {
126+
func distinct() -> [Iterator.Element] {
127+
var seen: Set<Iterator.Element> = []
128+
return filter { seen.insert($0).inserted }
129+
}
130+
}

Tests/CodeOwnersMacroTests/CodeOwnerMacroTest.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ struct CodeOwnersMacrosTest {
3131

3232
assertMacroExpansion(
3333
"""
34-
let CODEOWNERS: Set<String> = #codeOwners
34+
let CODEOWNERS: [String] = #codeOwners
3535
""",
3636
expandedSource: """
37-
let CODEOWNERS: Set<String> = \(ownersLiteral.map { "[\($0)]" } ?? "nil")
37+
let CODEOWNERS: [String] = \(ownersLiteral.map { "[\($0)]" } ?? "nil")
3838
""",
3939
macroSpecs: [ "codeOwners" : MacroSpec(type: CodeOwnersMacroTestImpl.self) ],
4040
testFileName: params.file

0 commit comments

Comments
 (0)