Skip to content

CI: Add SwiftLint (full ruleset + concurrency guardrail custom rules) #45

Description

@itsniper

Goal

Wire SwiftLint into CI as the "lint" pillar of #21. This is split out from the parent CI issue because, unlike the build/test and DocC gates, it involves manual review and per-rule decisions: enabling SwiftLint's full default ruleset will surface violations in existing code that must be auto-corrected, hand-fixed, or deliberately configured away before the check can go green.

Split out from #21 so it can be addressed independently. The swift build / swift test job and the CI badge are tracked directly on #21; the DocC gate is already done (#22, #33).

Context

See docs/investigations/swift6-concurrency-audit-2026-05-13.mdPreventive Measures. The audit calls for banning @unchecked Sendable and @preconcurrency import outside Sources/ReliaBLEMock/ (where retroactive/alias annotations are legitimate), enforced in CI so violations break the build rather than review.

Decision already made: run the full default ruleset plus the two custom rules, not just the custom guardrails.

Deliverables

  1. .swiftlint.yml at repo root:

    • custom_rules — the two rules verbatim from the audit / NFR 11: Continuous Integration (GitHub Actions) #21, scoped included: "Sources/ReliaBLE/.*\\.swift", severity: error:
      custom_rules:
        no_unchecked_sendable:
          name: "@unchecked Sendable banned"
          regex: '@unchecked\s+Sendable'
          included: "Sources/ReliaBLE/.*\\.swift"
          severity: error
        no_preconcurrency_import:
          name: "@preconcurrency import banned"
          regex: '@preconcurrency\s+import'
          included: "Sources/ReliaBLE/.*\\.swift"
          severity: error
    • Enable the default built-in ruleset (do not blanket-disable).
    • included: the library package only — Sources, Tests.
    • excluded: Demo (separate project with its own looser conventions per CLAUDE.md), .build, docs.
    • Tune to fit the existing codebase rather than churn it: set line_length to the repo's actual max and add rules to disabled_rules only where they conflict with established, intentional style. Prefer swiftlint --fix for autocorrectable violations.
  2. lint job in .github/workflows/ci.yml (alongside the existing docc / deploy-docs jobs):

    • runs-on: macos-15, actions/checkout@v4.
    • Install SwiftLint (Homebrew); pin/note the version for reproducibility.
    • Run swiftlint lint --strict so any remaining violation (warning or error) fails the build — matching the --warnings-as-errors philosophy of the DocC job.
    • Runs on every PR and push to master.
  3. Fix existing violations so the lint job is green on master (the bulk of the manual work — iterative).

Acceptance criteria

  • .swiftlint.yml exists with the two custom rules verbatim + the default ruleset enabled, Demo excluded.
  • The lint job runs on every PR and passes on current master.
  • The two custom rules are verified to fire: @unchecked Sendable / @preconcurrency import in Sources/ReliaBLE/ errors, but the same in Sources/ReliaBLEMock/ does not.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions