Skip to content

Commit 3f4a672

Browse files
authored
Surfacing swift package warnings (#16)
* Surfacing swift package warnings * Renaming and better warning formatting --------- Co-authored-by: Alex Guretzki <[email protected]>
1 parent 458ac08 commit 3f4a672

12 files changed

+94
-55
lines changed

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/Helpers/PackageFileHelper.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,8 @@ struct PackageFileHelper {
5959
return Set(targets.map(\.name))
6060
}
6161

62-
func availableProducts(at projectDirectoryPath: String) throws -> Set<String> {
63-
let packageDescription = try packageDescription(at: projectDirectoryPath)
64-
return Set(packageDescription.products.map(\.name))
62+
func packageDescription(at projectDirectoryPath: String) throws -> SwiftPackageDescription {
63+
try generatePackageDescription(at: projectDirectoryPath)
6564
}
6665

6766
/// Inserts a new library into the targets section containing all targets from the target section
@@ -92,7 +91,7 @@ struct PackageFileHelper {
9291

9392
private extension PackageFileHelper {
9493

95-
func packageDescription(at projectDirectoryPath: String) throws -> SwiftPackageDescription {
94+
func generatePackageDescription(at projectDirectoryPath: String) throws -> SwiftPackageDescription {
9695

9796
let result = try xcodeTools.loadPackageDescription(projectDirectoryPath: projectDirectoryPath)
9897

@@ -114,7 +113,11 @@ private extension PackageFileHelper {
114113
}
115114

116115
if firstLine.starts(with: warningTag) {
117-
warnings += [firstLine]
116+
let directoryTag = "'\(URL(filePath: projectDirectoryPath).lastPathComponent)': "
117+
let warning = firstLine
118+
.replacingOccurrences(of: warningTag, with: "")
119+
.replacingOccurrences(of: directoryTag, with: "", options: .caseInsensitive)
120+
warnings += [warning]
118121
}
119122

120123
if firstLine.starts(with: "{"),

Sources/Pipeline/Modules/MarkdownOutputGenerator.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ struct MarkdownOutputGenerator: OutputGenerating {
1414
from changesPerTarget: [String: [Change]],
1515
allTargets: [String],
1616
oldSource: ProjectSource,
17-
newSource: ProjectSource
17+
newSource: ProjectSource,
18+
warnings: [String]
1819
) -> String {
1920

2021
let separator = "\n---"
@@ -26,6 +27,10 @@ struct MarkdownOutputGenerator: OutputGenerating {
2627
separator
2728
]
2829

30+
if !warnings.isEmpty {
31+
lines += Self.warningInfo(for: warnings) + [separator]
32+
}
33+
2934
if !changes.isEmpty {
3035
lines += changes + [separator]
3136
}
@@ -60,6 +65,10 @@ private extension MarkdownOutputGenerator {
6065
"**Analyzed targets:** \(allTargets.joined(separator: ", "))"
6166
}
6267

68+
static func warningInfo(for warnings: [String]) -> [String] {
69+
warnings.map { "> [!WARNING]\n> \($0)" }
70+
}
71+
6372
static func changeLines(changesPerModule: [String: [Change]]) -> [String] {
6473
var lines = [String]()
6574

Sources/Pipeline/Modules/LibraryAnalyzer.swift renamed to Sources/Pipeline/Modules/SwiftPackageFileAnalyzer.swift

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import Foundation
88

9-
struct LibraryAnalyzer: LibraryAnalyzing {
9+
struct SwiftPackageFileAnalyzer: ProjectAnalyzing {
1010

1111
let fileHandler: FileHandling
1212
let xcodeTools: XcodeTools
@@ -19,7 +19,7 @@ struct LibraryAnalyzer: LibraryAnalyzing {
1919
self.xcodeTools = xcodeTools
2020
}
2121

22-
func analyze(oldProjectUrl: URL, newProjectUrl: URL) throws -> [Change] {
22+
func analyze(oldProjectUrl: URL, newProjectUrl: URL) throws -> ProjectAnalyzerResult {
2323

2424
let oldProjectPath = oldProjectUrl.path()
2525
let newProjectPath = newProjectUrl.path()
@@ -34,18 +34,24 @@ struct LibraryAnalyzer: LibraryAnalyzing {
3434
)
3535

3636
return try analyze(
37-
old: packageHelper.availableProducts(at: oldProjectPath),
38-
new: packageHelper.availableProducts(at: newProjectPath)
37+
old: packageHelper.packageDescription(at: oldProjectPath),
38+
new: packageHelper.packageDescription(at: newProjectPath)
3939
)
4040
} else {
41-
return []
41+
return .init(
42+
changes: [],
43+
warnings: []
44+
)
4245
}
4346
}
4447

4548
private func analyze(
46-
old oldLibraries: Set<String>,
47-
new newLibraries: Set<String>
48-
) throws -> [Change] {
49+
old oldPackageDescription: SwiftPackageDescription,
50+
new newPackageDescription: SwiftPackageDescription
51+
) throws -> ProjectAnalyzerResult {
52+
53+
let oldLibraries = Set(oldPackageDescription.products.map(\.name))
54+
let newLibraries = Set(newPackageDescription.products.map(\.name))
4955

5056
let removedLibaries = oldLibraries.subtracting(newLibraries)
5157
var packageChanges = [Change]()
@@ -65,6 +71,9 @@ struct LibraryAnalyzer: LibraryAnalyzing {
6571
)
6672
}
6773

68-
return packageChanges
74+
return .init(
75+
changes: packageChanges,
76+
warnings: newPackageDescription.warnings
77+
)
6978
}
7079
}

Sources/Pipeline/Pipeline+Protocols.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,22 @@ protocol OutputGenerating {
3232
from changesPerTarget: [String: [Change]],
3333
allTargets: [String],
3434
oldSource: ProjectSource,
35-
newSource: ProjectSource
35+
newSource: ProjectSource,
36+
warnings: [String]
3637
) throws -> String
3738
}
3839

39-
protocol LibraryAnalyzing {
40+
struct ProjectAnalyzerResult {
41+
let changes: [Change]
42+
let warnings: [String]
43+
}
44+
45+
protocol ProjectAnalyzing {
4046
/// Analyzes whether or not the available libraries changed between the old and new version
4147
func analyze(
4248
oldProjectUrl: URL,
4349
newProjectUrl: URL
44-
) throws -> [Change]
50+
) throws -> ProjectAnalyzerResult
4551
}
4652

4753
enum LogLevel {

Sources/Pipeline/Pipeline.swift

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct Pipeline {
1414

1515
let projectBuilder: any ProjectBuilding
1616
let abiGenerator: any ABIGenerating
17-
let libraryAnalyzer: any LibraryAnalyzing
17+
let projectAnalyzer: any ProjectAnalyzing
1818
let sdkDumpGenerator: any SDKDumpGenerating
1919
let sdkDumpAnalyzer: any SDKDumpAnalyzing
2020
let outputGenerator: any OutputGenerating
@@ -26,7 +26,7 @@ struct Pipeline {
2626
scheme: String?,
2727
projectBuilder: any ProjectBuilding,
2828
abiGenerator: any ABIGenerating,
29-
libraryAnalyzer: any LibraryAnalyzing,
29+
projectAnalyzer: any ProjectAnalyzing,
3030
sdkDumpGenerator: any SDKDumpGenerating,
3131
sdkDumpAnalyzer: any SDKDumpAnalyzing,
3232
outputGenerator: any OutputGenerating,
@@ -37,7 +37,7 @@ struct Pipeline {
3737
self.scheme = scheme
3838
self.projectBuilder = projectBuilder
3939
self.abiGenerator = abiGenerator
40-
self.libraryAnalyzer = libraryAnalyzer
40+
self.projectAnalyzer = projectAnalyzer
4141
self.sdkDumpGenerator = sdkDumpGenerator
4242
self.sdkDumpAnalyzer = sdkDumpAnalyzer
4343
self.outputGenerator = outputGenerator
@@ -53,11 +53,13 @@ struct Pipeline {
5353
)
5454

5555
var changes = [String: [Change]]()
56+
var warnings = [String]()
5657

5758
try analyzeLibraryChanges(
5859
oldProjectUrl: oldProjectUrl,
5960
newProjectUrl: newProjectUrl,
60-
changes: &changes
61+
changes: &changes,
62+
warnings: &warnings
6163
)
6264

6365
let allTargets = try analyzeApiChanges(
@@ -70,7 +72,8 @@ struct Pipeline {
7072
from: changes,
7173
allTargets: allTargets.sorted(),
7274
oldSource: oldProjectSource,
73-
newSource: newProjectSource
75+
newSource: newProjectSource,
76+
warnings: warnings
7477
)
7578
}
7679
}
@@ -124,16 +127,18 @@ private extension Pipeline {
124127
return allTargets.sorted()
125128
}
126129

127-
func analyzeLibraryChanges(oldProjectUrl: URL, newProjectUrl: URL, changes: inout [String: [Change]]) throws {
130+
func analyzeLibraryChanges(oldProjectUrl: URL, newProjectUrl: URL, changes: inout [String: [Change]], warnings: inout [String]) throws {
128131
// Analyzing if there are any changes in available libraries between the project versions
129-
let libraryChanges = try libraryAnalyzer.analyze(
132+
let projectChanges = try projectAnalyzer.analyze(
130133
oldProjectUrl: oldProjectUrl,
131134
newProjectUrl: newProjectUrl
132135
)
133136

134-
if !libraryChanges.isEmpty {
135-
changes[""] = libraryChanges
137+
if !projectChanges.changes.isEmpty {
138+
changes[""] = projectChanges.changes
136139
}
140+
141+
warnings = projectChanges.warnings
137142
}
138143

139144
func analyzeApiChanges(oldProjectUrl: URL, newProjectUrl: URL, changes: inout [String: [Change]]) throws -> [String] {

Sources/public-api-diff.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ internal extension Pipeline {
7676
scheme: scheme,
7777
projectBuilder: ProjectBuilder(baseWorkingDirectoryPath: workingDirectoryPath, logger: logger),
7878
abiGenerator: ABIGenerator(logger: logger),
79-
libraryAnalyzer: LibraryAnalyzer(),
79+
projectAnalyzer: SwiftPackageFileAnalyzer(),
8080
sdkDumpGenerator: SDKDumpGenerator(),
8181
sdkDumpAnalyzer: SDKDumpAnalyzer(),
8282
outputGenerator: MarkdownOutputGenerator(),

Tests/UnitTests/OutputGeneratorTests.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ class OutputGeneratorTests: XCTestCase {
2424
from: [:],
2525
allTargets: ["Target_1"],
2626
oldSource: .local(path: "old_source"),
27-
newSource: .local(path: "new_source")
27+
newSource: .local(path: "new_source"),
28+
warnings: []
2829
)
2930
XCTAssertEqual(output, expectedOutput)
3031
}
@@ -52,7 +53,8 @@ class OutputGeneratorTests: XCTestCase {
5253
from: ["Target_1": [.init(changeType: .addition(description: "Some Addition"), parentName: "")]],
5354
allTargets: ["Target_1"],
5455
oldSource: .local(path: "old_source"),
55-
newSource: .local(path: "new_source")
56+
newSource: .local(path: "new_source"),
57+
warnings: []
5658
)
5759
XCTAssertEqual(output, expectedOutput)
5860
}
@@ -102,7 +104,8 @@ class OutputGeneratorTests: XCTestCase {
102104
],
103105
allTargets: ["Target_1", "Target_2"],
104106
oldSource: .remote(branch: "old_branch", repository: "old_repository"),
105-
newSource: .local(path: "new_source")
107+
newSource: .local(path: "new_source"),
108+
warnings: []
106109
)
107110
XCTAssertEqual(output, expectedOutput)
108111
}

Tests/UnitTests/PipelineTests.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class PipelineTests: XCTestCase {
1717
let abiGeneratorExpectation = expectation(description: "ABIGenerator is called twice")
1818
abiGeneratorExpectation.expectedFulfillmentCount = 2
1919

20-
let libraryAnalyzerExpectation = expectation(description: "LibraryAnalyzer is called once")
20+
let projectAnalyzerExpectation = expectation(description: "ProjectAnalyzer is called once")
2121

2222
let dumpGeneratorExpectation = expectation(description: "SDKDumpGenerator is called twice")
2323
dumpGeneratorExpectation.expectedFulfillmentCount = 2
@@ -77,14 +77,17 @@ class PipelineTests: XCTestCase {
7777

7878
return [.init(targetName: "Target", abiJsonFileUrl: url)]
7979
}),
80-
libraryAnalyzer: MockLibraryAnalyzer(onAnalyze: { old, new in
80+
projectAnalyzer: MockProjectAnalyzer(onAnalyze: { old, new in
8181
XCTAssertEqual(old, expectedSteps.first as? URL)
8282
expectedSteps.removeFirst()
8383
XCTAssertEqual(new, expectedSteps.first as? URL)
8484
expectedSteps.removeFirst()
85-
libraryAnalyzerExpectation.fulfill()
85+
projectAnalyzerExpectation.fulfill()
8686

87-
return [.init(changeType: .addition(description: "A Library was added"), parentName: "Parent")]
87+
return .init(
88+
changes: [.init(changeType: .addition(description: "A Library was added"), parentName: "Parent")],
89+
warnings: []
90+
)
8891
}),
8992
sdkDumpGenerator: MockSDKDumpGenerator(onGenerate: { url in
9093
XCTAssertEqual(url, expectedSteps.first as? URL)
@@ -102,7 +105,7 @@ class PipelineTests: XCTestCase {
102105

103106
return [.init(changeType: .addition(description: "Something was added"), parentName: "Parent")]
104107
}),
105-
outputGenerator: MockOutputGenerator(onGenerate: { changes, allTargets, old, new in
108+
outputGenerator: MockOutputGenerator(onGenerate: { changes, allTargets, old, new, warnings in
106109
XCTAssertEqual(changes, expectedSteps.first as? [String: [Change]])
107110
expectedSteps.removeFirst()
108111
XCTAssertEqual(allTargets, expectedSteps.first as? [String])
@@ -111,6 +114,7 @@ class PipelineTests: XCTestCase {
111114
expectedSteps.removeFirst()
112115
XCTAssertEqual(new, expectedSteps.first as? ProjectSource)
113116
expectedSteps.removeFirst()
117+
XCTAssertTrue(warnings.isEmpty)
114118

115119
return "Output"
116120
}),
@@ -122,7 +126,7 @@ class PipelineTests: XCTestCase {
122126
await fulfillment(of: [
123127
projectBuilderExpectation,
124128
abiGeneratorExpectation,
125-
libraryAnalyzerExpectation,
129+
projectAnalyzerExpectation,
126130
dumpGeneratorExpectation,
127131
dumpAnalyzerExpectation
128132
])

0 commit comments

Comments
 (0)