Skip to content

Adopt SwiftLint: pre-commit hook + CI + violation cleanup#5

Merged
odrobnik merged 2 commits into
mainfrom
claude/modest-hoover-4f8acb
May 15, 2026
Merged

Adopt SwiftLint: pre-commit hook + CI + violation cleanup#5
odrobnik merged 2 commits into
mainfrom
claude/modest-hoover-4f8acb

Conversation

@odrobnik
Copy link
Copy Markdown
Contributor

Summary

Adopts SwiftLint on ShellKit, following the field-notes pattern (shareable in-repo hook + CI workflow + initial violation cleanup).

  • Pre-commit hook (scripts/hooks/pre-commit, installed via scripts/install-hooks.sh) — auto-fixes staged .swift files, refuses to run when staged files have unstaged edits, then strict-lints what remains. CI-skip via \$CI env var.
  • CI workflow (.github/workflows/swiftlint.yml) — macOS + Homebrew SwiftLint so the binary/rule-set matches the hook (no version drift). Path-filtered to .swift / .swiftlint.yml / the workflow file.
  • .swiftlint.yml — minimal: Sources + Tests included; .build / .swiftpm excluded. No app target on this library package.
  • Initial cleanup — 132 violations → 0, all renames are local-only (no public-API parameter labels touched).

The two commits split cleanly along that boundary:

  1. SwiftLint: adopt as pre-commit hook and CI workflow — infrastructure only
  2. SwiftLint: clear initial violations to pass --strict — 21-file cleanup

Cleanup breakdown (per iBash field-notes triage)

Rule Hits Approach
auto-fixable 24 swiftlint --fix (commas/braces/trailing newlines/implicit-optional-init)
identifier_name 78 Local renames — sbsandbox, rcresult, hlower, i/vindex/value, ncount/name, bp/okbuffer/success, eentry, etc. No public-API breakage.
optional_data_string_conversion 15 Library streaming sites: surgical // swiftlint:disable:next with "lossy by design" reason. Test sites: switched to String(bytes:encoding:) ?? \"\" (rule's preferred form).
large_tuple 3 New structs: PrivateIP.IPv4Address, HostInfo.UnameInfo.
function_body_length 2 DefaultProcessLauncher.launch split into 4 helpers; HostInfo.real split into realUserAndFull + realSupplementaryGroups.
function_parameter_count 5 Surgical disables on the launcher protocol + 4 conformers — the 7-arg signature mirrors POSIX exec; bundling into a struct breaks every consumer.
cyclomatic_complexity 1 isPrivateIPv4 refactored (top-half handles whole-first-octet ranges, helper handles octet-pair ranges) — no disable needed.
for_where 3 Mechanical for X in Y where Z.
nesting 1 Entry.State disabled with reason — meaningful grouping.

Test plan

  • swift build --build-tests clean
  • swift test — 33/33 passing
  • swiftlint lint --strict exit 0
  • After merge: contributors run scripts/install-hooks.sh once to activate the hook locally (or skip it — CI catches anything that lands without the hook).

Reviewer notes

  • The hook is not auto-activated; core.hooksPath is set by scripts/install-hooks.sh, which is a one-time-per-clone explicit step.
  • No public-API surface changed. The launcher protocol signature is preserved (with swiftlint:disable:this on the protocol method's func launch( line); all 4 conforming implementations keep the same 7-parameter shape.
  • Pre-existing diagnostics in ShellTests.swift (4 try warnings on withCurrent calls) are unrelated to this branch — they existed on main before this PR.

🤖 Generated with Claude Code

odrobnik and others added 2 commits May 15, 2026 14:26
Adds a shareable pre-commit hook under scripts/hooks/ that auto-fixes
staged .swift files, refuses to run when staged files have unstaged
edits, and strict-lints what remains. scripts/install-hooks.sh wires
core.hooksPath -> scripts/hooks (one-time per clone).

CI runs the same Homebrew SwiftLint on macos-latest so the binary and
rule set match the local hook — no version drift between dev and CI.
Path filter scopes the workflow to .swift / .swiftlint.yml / the
workflow file itself.

Minimal .swiftlint.yml at the root includes Sources + Tests; ShellKit
is a library-only package so there's no app target to list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
132 → 0. Auto-fix cleared the easy 24 (commas, braces, trailing
newlines). Remaining 108 broken down per the iBash field-notes
triage:

- 78 identifier_name: all local-variable renames — no public-API
  parameter labels touched (every short-name violation is on a let,
  closure param, or case binding). Common renames: sb → sandbox,
  rc → result, h → lower/headStr, i/v → index/value, n → count,
  bp/ok → buffer/success, e → entry.

- 15 optional_data_string_conversion: library streaming-decode sites
  get surgical `// swiftlint:disable:next` with reasons — the rule's
  preferred failable form would drop bytes on partial UTF-8 chunks,
  which is wrong for InputSource/OutputSink contracts. Test sites
  switched to the rule's preferred `String(bytes:encoding:) ?? ""`
  form (cleaner than a disable per call site).

- 3 large_tuple: new local structs PrivateIP.IPv4Address (4-octet
  return shape) and HostInfo.UnameInfo (5-field uname result).

- 2 function_body_length: DefaultProcessLauncher.launch split into
  four private helpers (collectInputBytes, subprocessExecutable,
  subprocessEnvironment, resolveWorkingDirectory). HostInfo.real
  split into realUserAndFull + realSupplementaryGroups.

- 5 function_parameter_count: surgical disables on the launcher
  protocol + 4 conformers. The 7-arg signature mirrors POSIX exec
  (program + args + env + cwd + stdin + stdout + stderr); bundling
  into a struct is a breaking protocol change for every ShellKit
  consumer for no behavioural gain.

- 1 cyclomatic_complexity: PrivateIP.isPrivateIPv4 refactored into
  a top-half that handles whole-first-octet ranges and a private
  isReservedIPv4OctetPair helper for the octet-pair ranges (link-
  local, 172.16/12, 192.168/16, 192.0/24, CGNAT). No disable.

- 3 for_where, 1 nesting (Entry.State, disabled with reason),
  remaining mechanical rewrites.

`swift build` clean, `swift test` 33/33 passing, `swiftlint --strict`
exit 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@odrobnik odrobnik merged commit d90e0a3 into main May 15, 2026
6 checks passed
@odrobnik odrobnik deleted the claude/modest-hoover-4f8acb branch May 15, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant