Make SwiftLint blocking in CI now that the violation backlog is zero (#1646)

* Make SwiftLint blocking in CI now that the violation backlog is zero

The lint job has been advisory since #1361, with the backlog tracked in
#1088. That backlog is now down to 28 violations, so this clears it and
flips the job to enforcing instead of adding baseline machinery:

- Fix all remaining violations: closure parameters/capture lists joined
  onto their opening-brace line (BLEService, NoiseSessionManager,
  ChatMediaTransferCoordinator, BLENoisePacketHandlerTests), one
  trailing comma, one opening-brace spacing (both via swiftlint --fix).
- Move the two periphery:ignore comments above the doc blocks they were
  orphaning; verified with a periphery --strict scan that the commands
  still register (no new findings vs baseline).
- Disable the todo rule: the single TODO cites tracked issue #1434 and
  is a deliberate marker, not lint debt.
- Exclude .device-lab from local runs (untracked; CI never sees it).
- CI: drop continue-on-error and the "(advisory)" label; run with
  --strict so any new warning fails the job.

Full SwiftPM suite green (2,004 tests / 216 suites) after the
formatting changes; swiftlint lint --strict exits 0 with the same
0.65.0 the CI container pins.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Fix is_disjoint violation introduced by #1542

The strict lint gate caught this on its first run: the mention-suggestion
key handler used modifierFlags.intersection([...]).isEmpty, which is
exactly the pattern isDisjoint(with:) expresses directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jack 2026-08-08 12:12:30 +02:00 committed by GitHub
parent 9c84d4ce4f
commit 2a9fb4d53f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 28 additions and 35 deletions

View File

@ -264,11 +264,13 @@ jobs:
CODE_SIGNING_ALLOWED=NO \
test
# Advisory only: SwiftLint reports style violations without ever failing the
# build. Runs in a pinned container (no Xcode plugin, no pbxproj changes) so
# it can never break the documented xcodebuild path or block a merge.
# Blocking since the backlog reached zero (all violations fixed; rules the
# codebase deliberately breaks are disabled in .swiftlint.yml). --strict
# promotes warnings to errors so new violations fail the job. Runs in a
# pinned container (no Xcode plugin, no pbxproj changes) so it can never
# break the documented xcodebuild path.
lint:
name: SwiftLint (advisory)
name: SwiftLint
runs-on: ubuntu-latest
timeout-minutes: 15
# This job runs a third-party container image, so give it the least
@ -280,10 +282,9 @@ jobs:
# Tag for readability, digest for immutability (tags can be repointed).
# Bump both together, deliberately — never a floating tag.
image: ghcr.io/realm/swiftlint:0.65.0@sha256:a482729f4b58741875af1566f23397f3f6db300372756fc31606d0a4527fab9e
continue-on-error: true
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Run SwiftLint
run: swiftlint lint --reporter github-actions-logging
run: swiftlint lint --strict --reporter github-actions-logging

View File

@ -3,6 +3,7 @@
excluded:
- .build
- .claude
- .device-lab
- .swiftpm
- .DerivedData
- DerivedData
@ -21,6 +22,7 @@ disabled_rules:
- control_statement
- void_function_in_ternary
- redundant_discardable_let # SwiftUI breaks without it
- todo # TODOs that cite a tracked issue are deliberate markers, not lint debt
# To be enabled as we fix the issues
- trailing_whitespace
- cyclomatic_complexity

View File

@ -1001,8 +1001,7 @@ final class NoiseSessionManager {
0,
recentInitiatorCompletionGracePeriod - elapsed
)
let timeout = DispatchWorkItem(flags: .barrier) {
[weak self, weak establishedSession] in
let timeout = DispatchWorkItem(flags: .barrier) { [weak self, weak establishedSession] in
guard let self,
let establishedSession,
let current = self.sessions[peerID],

View File

@ -4027,10 +4027,8 @@ extension BLEService {
)
}
}
service.onRekeyHandshakeReady = {
[weak self, weak service] peerID, initiation in
self?.messageQueue.async {
[weak self, weak service] in
service.onRekeyHandshakeReady = { [weak self, weak service] peerID, initiation in
self?.messageQueue.async { [weak self, weak service] in
guard let self,
let service,
self.noiseService === service else {
@ -4046,14 +4044,12 @@ extension BLEService {
self.broadcastNoiseHandshake(message, to: peerID)
}
}
service.onHandshakeRecoveryRequired = {
[weak self, weak service] request in
service.onHandshakeRecoveryRequired = { [weak self, weak service] request in
guard let self, let service else { return }
#if DEBUG
self._test_beforeHandshakeRecoveryEnqueued?(request.peerID)
#endif
self.messageQueue.async {
[weak self, weak service] in
self.messageQueue.async { [weak self, weak service] in
guard let self,
let service,
self.noiseService === service else {
@ -5294,8 +5290,7 @@ extension BLEService {
) else {
return
}
messageQueue.async {
[weak self, weak service] in
messageQueue.async { [weak self, weak service] in
guard let self,
let service,
self.noiseService === service,

View File

@ -108,6 +108,6 @@ struct QuickJoinSuggestion {
"UY": "6c", "UZ": "tx", "VA": "sr", "VC": "dd",
"VE": "d9", "VI": "de", "VN": "w7", "VU": "rs",
"WS": "2j", "XK": "sr", "YE": "sf", "YT": "mj",
"ZA": "ke", "ZM": "kt", "ZW": "ks",
"ZA": "ke", "ZM": "kt", "ZW": "ks"
]
}

View File

@ -1859,8 +1859,7 @@ private extension ChatMediaTransferCoordinator {
TimeInterval(UInt64.max) / 1_000_000_000
) * 1_000_000_000
)
reconnectRetryExpiryTasks[messageID] = Task {
@MainActor [weak self] in
reconnectRetryExpiryTasks[messageID] = Task { @MainActor [weak self] in
if nanoseconds > 0 {
try? await Task.sleep(nanoseconds: nanoseconds)
}

View File

@ -482,7 +482,7 @@ private struct AutocompleteKeyboardNavigationModifier: ViewModifier {
/// list is up. Inactive monitors pass everything through.
private func handleKeyDown(_ event: NSEvent) -> NSEvent? {
guard isActive(),
event.modifierFlags.intersection([.command, .option, .control]).isEmpty else {
event.modifierFlags.isDisjoint(with: [.command, .option, .control]) else {
return event
}

View File

@ -653,8 +653,7 @@ struct ChatMediaTransferCoordinatorContextTests {
@Test @MainActor
func deleteStableMediaReleasesRetainedRetryBeforeTombstoneCommit()
async throws
{
async throws {
let context = MockChatMediaTransferContext()
let peerID = PeerID(str: "1122334455667788")
context.selectedPrivateChatPeer = peerID

View File

@ -150,14 +150,12 @@ struct BLENoisePacketHandlerTests {
recorder.clearedSessions.append(peerID)
service.clearSession(for: peerID)
},
handleAuthenticatedPeerState: {
peerID, payload, generation in
handleAuthenticatedPeerState: { peerID, payload, generation in
recorder.authenticatedPeerStates.append(
(peerID, payload, generation)
)
},
deliverNoisePayload: {
peerID, type, payload, timestamp in
deliverNoisePayload: { peerID, type, payload, timestamp in
recorder.deliveries.append(
(peerID, type, payload, timestamp)
)

View File

@ -8,6 +8,9 @@
import Foundation
// periphery:ignore - intentionally unreferenced by production code; nothing
// emits or consumes this type yet, and BLEService parses it only to ignore it.
// Delete this annotation when the mesh starts using it.
/// Identity-free presence announcement for rotating peer IDs.
///
/// The v1 `AnnouncementPacket` broadcasts, in cleartext, every 430 seconds: the
@ -37,9 +40,6 @@ import Foundation
/// `docs/PEER-ID-ROTATION.md`.
///
/// Not emitted or consumed by the shipping mesh yet.
// periphery:ignore - intentionally unreferenced by production code; nothing
// emits or consumes this type yet, and BLEService parses it only to ignore it.
// Delete this annotation when the mesh starts using it.
public struct AnnounceV2Packet: Equatable, Sendable {
/// Rotation epoch this announce was built for. Carried explicitly so a
/// receiver matches against a stated epoch instead of guessing.

View File

@ -9,6 +9,10 @@
import Foundation
private import CryptoKit
// periphery:ignore - intentionally unreferenced by production code. These are
// the reviewable primitives for a protocol change that cannot ship until both
// platforms agree on it; wiring them into the transport is the next step, not
// this one. Delete this annotation when the mesh starts using them.
/// Derivations for rotating peer IDs and pairwise recognition tags.
///
/// See `docs/PEER-ID-ROTATION.md` for the design, the threat model, and the
@ -32,10 +36,6 @@ private import CryptoKit
/// Recognition tags come from the X25519 shared secret between two static
/// keys, so exactly two parties can compute a given tag and an observer can
/// compute none of them.
// periphery:ignore - intentionally unreferenced by production code. These are
// the reviewable primitives for a protocol change that cannot ship until both
// platforms agree on it; wiring them into the transport is the next step, not
// this one. Delete this annotation when the mesh starts using them.
public enum PeerIDRotation {
// MARK: - Parameters