Skip to content

Commit ad955b0

Browse files
juntakiclaude
andcommitted
Split ReproduceCommand.run() into a helper; gate unused imports with swiftlint analyze
ReproduceCommand.run() split into ReproduceCommand+Execution.swift, no behavior change. Adds a swiftlint analyze CI job (unused_import only) gated into the merge gate, after removing the 74 unused imports it found across the tree. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 660e4be commit ad955b0

64 files changed

Lines changed: 260 additions & 308 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,45 @@ jobs:
163163
shell: bash
164164
run: swift-complexity Sources Tests --recursive --threshold 25 --report-suppressions
165165

166+
# Compiler-aware lint: `unused_import` only (see .swiftlint.yml's own
167+
# `analyzer_rules` comment). Needs a clean xcodebuild log, so it pays for
168+
# a full `MutantKit-Package` build where `lint` needs none — a separate
169+
# job rather than a step there, so the fast static checks and this gate
170+
# never block each other. The pre-existing set (74 true positives on this
171+
# tree, not the 66 a syntax-only per-file scan over a *different* file set
172+
# would suggest -- private targets excluded from the public snapshot
173+
# change the count) was removed outright before this gate was switched on,
174+
# so any finding here is new debt.
175+
analyze:
176+
name: Unused imports (swiftlint analyze)
177+
runs-on: macos-15
178+
timeout-minutes: 20
179+
env:
180+
SWIFTLINT_VERSION: "0.63.2"
181+
steps:
182+
- uses: actions/checkout@v4
183+
- name: Install pinned SwiftLint
184+
run: |
185+
curl -fsSL -o portable_swiftlint.zip \
186+
"https://github.com/realm/SwiftLint/releases/download/${SWIFTLINT_VERSION}/portable_swiftlint.zip"
187+
unzip -o portable_swiftlint.zip swiftlint
188+
chmod +x swiftlint
189+
sudo mv swiftlint /usr/local/bin/swiftlint
190+
rm portable_swiftlint.zip
191+
installed_version="$(swiftlint version)"
192+
if [ "$installed_version" != "$SWIFTLINT_VERSION" ]; then
193+
echo "::error::Installed SwiftLint $installed_version does not match pinned SWIFTLINT_VERSION $SWIFTLINT_VERSION"
194+
exit 1
195+
fi
196+
- name: Clean build for analyzer
197+
shell: bash
198+
run: |
199+
set -o pipefail
200+
xcodebuild -scheme MutantKit-Package -destination 'generic/platform=macOS' clean build 2>&1 | tee xcodebuild.log
201+
- name: swiftlint analyze
202+
shell: bash
203+
run: swiftlint analyze --strict --config .swiftlint.yml --compiler-log-path xcodebuild.log Sources
204+
166205
# Fast feedback: everything that does not build another project.
167206
unit:
168207
name: Unit tests
@@ -740,6 +779,7 @@ jobs:
740779
needs:
741780
- lint
742781
- complexity
782+
- analyze
743783
- unit
744784
- acceptance
745785
- ror-schemata-differential
@@ -763,6 +803,7 @@ jobs:
763803
764804
check "lint" "${{ needs.lint.result }}"
765805
check "complexity" "${{ needs.complexity.result }}"
806+
check "analyze" "${{ needs.analyze.result }}"
766807
check "unit" "${{ needs.unit.result }}"
767808
check "acceptance" "${{ needs.acceptance.result }}"
768809
check "ror-schemata-differential" "${{ needs.ror-schemata-differential.result }}"

.swiftlint-baseline.json

Lines changed: 5 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"ruleName": "Type Body Length",
77
"location": {
88
"file": "Sources/AppleBuildAdapters/SimulatorPool.swift",
9-
"line": 152,
9+
"line": 150,
1010
"character": 8
1111
},
1212
"reason": "Actor body should span 300 lines or less excluding comments and whitespace: currently spans 341 lines",
@@ -21,11 +21,11 @@
2121
"ruleName": "Function Body Length",
2222
"location": {
2323
"file": "Sources/AppleBuildAdapters/XcodeBuildAdapter.swift",
24-
"line": 1560,
24+
"line": 1606,
2525
"character": 12
2626
},
2727
"reason": "Function body should span 80 lines or less excluding comments and whitespace: currently spans 87 lines",
28-
"severity": "error"
28+
"severity": "warning"
2929
},
3030
"text": " public func runBatch("
3131
},
@@ -81,44 +81,14 @@
8181
"ruleName": "Unused Enumerated",
8282
"location": {
8383
"file": "Sources/BudgetV2Eval/Statistics.swift",
84-
"line": 30,
84+
"line": 34,
8585
"character": 29
8686
},
8787
"reason": "When the index is not used, `.enumerated()` can be removed",
8888
"severity": "error"
8989
},
9090
"text": " let sorted = pooled.enumerated().sorted { $0.element.value < $1.element.value }"
9191
},
92-
{
93-
"violation": {
94-
"ruleIdentifier": "cyclomatic_complexity",
95-
"ruleDescription": "Complexity of function bodies should be limited.",
96-
"ruleName": "Cyclomatic Complexity",
97-
"location": {
98-
"file": "Sources/CLI/Commands/ReproduceCommand.swift",
99-
"line": 38,
100-
"character": 5
101-
},
102-
"reason": "Function should have complexity 12 or less; currently complexity is 13",
103-
"severity": "error"
104-
},
105-
"text": " func run() async throws {"
106-
},
107-
{
108-
"violation": {
109-
"ruleIdentifier": "function_body_length",
110-
"ruleDescription": "Function bodies should not span too many lines",
111-
"ruleName": "Function Body Length",
112-
"location": {
113-
"file": "Sources/CLI/Commands/ReproduceCommand.swift",
114-
"line": 38,
115-
"character": 5
116-
},
117-
"reason": "Function body should span 80 lines or less excluding comments and whitespace: currently spans 135 lines",
118-
"severity": "error"
119-
},
120-
"text": " func run() async throws {"
121-
},
12292
{
12393
"violation": {
12494
"ruleIdentifier": "type_body_length",
@@ -621,7 +591,7 @@
621591
"ruleName": "Line Length",
622592
"location": {
623593
"file": "Sources/MutationModel/VerdictProof.swift",
624-
"line": 31,
594+
"line": 29,
625595
"character": 1
626596
},
627597
"reason": "Line should be 140 characters or less; currently it has 157 characters",
@@ -2038,35 +2008,5 @@
20382008
"severity": "error"
20392009
},
20402010
"text": "struct XCResultAdapterBatchTests {"
2041-
},
2042-
{
2043-
"violation": {
2044-
"ruleIdentifier": "line_length",
2045-
"ruleDescription": "Lines should not span too many characters.",
2046-
"ruleName": "Line Length",
2047-
"location": {
2048-
"file": "Tests/MutantKitTests/Unit/XcodeBuildAdapterUninstallFailureTests.swift",
2049-
"line": 66,
2050-
"character": 1
2051-
},
2052-
"reason": "Line should be 140 characters or less; currently it has 159 characters",
2053-
"severity": "error"
2054-
},
2055-
"text": " SimulatorLease(device: SimulatorDevice(udid: udid, name: \"Bogus\", runtimeIdentifier: \"com.apple.CoreSimulator.SimRuntime.iOS-18-0\", state: \"Shutdown\"))"
2056-
},
2057-
{
2058-
"violation": {
2059-
"ruleIdentifier": "line_length",
2060-
"ruleDescription": "Lines should not span too many characters.",
2061-
"ruleName": "Line Length",
2062-
"location": {
2063-
"file": "Tests/MutantKitTests/Unit/XcodeBuildAdapterUninstallFailureTests.swift",
2064-
"line": 73,
2065-
"character": 1
2066-
},
2067-
"reason": "Line should be 140 characters or less; currently it has 148 characters",
2068-
"severity": "error"
2069-
},
2070-
"text": " let message = XcodeBuildAdapter.uninstallFailureWarning(bundleID: \"com.example.App\", udid: \"ABCD-1234\", detail: \"Invalid device: ABCD-1234\")"
20712011
}
20722012
]

.swiftlint.yml

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,26 @@
99
# to stop erroring, left as a warning, documented here).
1010
#
1111
# CI runs `swiftlint lint --strict --baseline .swiftlint-baseline.json`:
12-
# the baseline freezes 138 pre-existing violations by identity (rule + file +
12+
# the baseline freezes 134 pre-existing violations by identity (rule + file +
1313
# line) so `--strict` still catches every new violation, including a new one
1414
# in an already-baselined file, without forcing a refactor of any of them
15-
# just to silence the linter. By rule: `line_length` 80 (mostly long test
15+
# just to silence the linter. By rule: `line_length` 78 (mostly long test
1616
# lines and interpolated diagnostics), `type_body_length` 16, `large_tuple`
17-
# 13, `function_body_length` 10, `function_parameter_count` 6,
18-
# `cyclomatic_complexity` 4, `file_length` 5, `force_try` 2, plus one
17+
# 13, `function_body_length` 9, `function_parameter_count` 6,
18+
# `file_length` 5, `cyclomatic_complexity` 3, `force_try` 2, plus one
1919
# `unused_enumerated` and one `orphaned_doc_comment`. The length/complexity
2020
# rules concentrate in a few deliberately large orchestration files
2121
# (MutationRunner.swift, SchemataMutationRunner.swift, XcodeBuildAdapter.swift
22-
# and similar), but `cyclomatic_complexity` also covers a ReproduceCommand
23-
# handler (13), ConfigurationValidation (13), DeclarationIdentityResolver
24-
# (15) and one simulator-pool test — and `large_tuple` is spread across
25-
# several test files. This count includes a handful of entries private-tree-
26-
# only targets (research/probe executables, benchmark harness) contribute
27-
# that the public projection's own baseline doesn't carry, since those
28-
# targets are excluded from the public snapshot. (Earlier versions of this
29-
# comment also named InspectCommand.run()/RunCommand.run(), and then
30-
# QualityGate.evaluate (16)/BudgetSelectorV2.allocateCounts (18); all four
31-
# have since been split far enough that none is baselined anymore.)
22+
# and similar), but `cyclomatic_complexity` also covers ConfigurationValidation
23+
# (13), DeclarationIdentityResolver (15) and one simulator-pool test — and
24+
# `large_tuple` is spread across several test files. This count includes a
25+
# handful of entries private-tree-only targets (research/probe executables,
26+
# benchmark harness) contribute that the public projection's own baseline
27+
# doesn't carry, since those targets are excluded from the public snapshot.
28+
# (Earlier versions of this comment also named InspectCommand.run()/
29+
# RunCommand.run(), then QualityGate.evaluate (16)/
30+
# BudgetSelectorV2.allocateCounts (18), then a ReproduceCommand handler (13);
31+
# all five have since been split far enough that none is baselined anymore.)
3232
#
3333
# Baseline entries key on line number, so an unrelated edit earlier in a
3434
# baselined file shifts every violation below it and makes CI fail on an
@@ -137,3 +137,10 @@ disabled_rules:
137137
# oversight — forcing the failable initializer here would just add an
138138
# unwrap for a case that can't happen.
139139
- optional_data_string_conversion
140+
141+
# Compiler-aware rules: these run ONLY under `swiftlint analyze` with a
142+
# clean xcodebuild log, never under `swiftlint lint`. Scope is deliberately
143+
# one rule: `unused_import` catches exactly the failure class a pure file
144+
# move introduces (a leftover import no syntax-only rule sees).
145+
analyzer_rules:
146+
- unused_import

Sources/AppleBuildAdapters/DestinationResolver.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import Foundation
2-
import MutationModel
32

43
/// One `-destination` specifier, resolved to a concrete device (or proven to
54
/// need none), once, at run start.

Sources/AppleBuildAdapters/SchemataBuiltImageInspection.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import Foundation
2-
import MutationExecution
32
import MutationModel
43

54
/// Every way `SchemataBuiltImageInspection.inspect` refuses to produce a

Sources/AppleBuildAdapters/SchemataRuntimeManifest.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import Foundation
2-
31
/// The shape of `lib/mutantkit/schemata/manifest.json` — the file a
42
/// released `mutantkit` tarball bundles alongside its per-platform
53
/// `MutantKitSchemataRuntime` archives (`scripts/release-build.sh` produces

Sources/AppleBuildAdapters/SimulatorPool.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import Darwin
21
import Foundation
32
import MutationExecution
4-
import MutationModel
53

64
/// One simulator, as `simctl` describes it.
75
public struct SimulatorDevice: Codable, Sendable, Hashable {

Sources/AppleBuildAdapters/SwiftPMCompilationUnitImageResolver.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
import Foundation
2-
import MutationExecution
3-
import MutationModel
4-
51
/// Every way the SwiftPM path refuses to map a compilation unit's target to
62
/// a real built image — fail-closed: an ambiguous or unproven mapping must
73
/// never be resolved by guessing.

Sources/AppleBuildAdapters/XcodeConfigDetector.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import Foundation
2-
import MutationExecution
32
import MutationModel
43

54
/// Real auto-detection of an Xcode project/workspace's scheme, test

Sources/CLI/Commands/ConfigCommand.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import ArgumentParser
2-
import Foundation
32
import MutationModel
43

54
/// Validates `mutantkit.yml` or emits the JSON Schema used by editors/CI.

0 commit comments

Comments
 (0)