From 21ea94610abedabb37bbcbbd82429e5d2a274847 Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Thu, 19 Feb 2026 10:55:43 -0500 Subject: [PATCH] macos: lint Swift files using SwiftLint SwiftLint is both a linter and formatting. It's a popular way to spot issues and enforce a consistent style. Our SwiftLint configuration lives in macos/.swiftlint.yml, where is is automatically discovered. It's very configurable, and I made an initial pass as some basic, weakly-opinionated rules. The "TODO" section lists rules that currently have violations but can be easily (auto)fixed in follow-up commits. Our integration is CLI-based. Similar to our other support tools, we expect developers to install `swiftlint` via nix or e.g. Homebrew. This is documented in HACKING.md. We also have an optional Xcode integration, for in-editor feedback. When `swiftlint` is available, it's run as a script-based Build Phase. SwiftLint supports an auto-fix mode (--fix). Agents are aware of this via AGENTS.md. The rules are enforced using a (nix-based) CI job. --- .github/workflows/test.yml | 22 +++++++++ AGENTS.md | 1 + HACKING.md | 25 ++++++++++ macos/.swiftlint.yml | 64 +++++++++++++++++++++++++ macos/Ghostty.xcodeproj/project.pbxproj | 24 ++++++++++ nix/devShell.nix | 4 ++ 6 files changed, 140 insertions(+) create mode 100644 macos/.swiftlint.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ca34cefd..bcc028dd4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,6 +36,7 @@ jobs: - test-macos - pinact - prettier + - swiftlint - alejandra - typos - shellcheck @@ -927,6 +928,27 @@ jobs: - name: prettier check run: nix develop -c prettier --check . + swiftlint: + if: github.repository == 'ghostty-org/ghostty' + runs-on: namespace-profile-ghostty-macos-tahoe + timeout-minutes: 60 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + # TODO(tahoe): https://github.com/NixOS/nix/issues/13342 + - uses: DeterminateSystems/nix-installer-action@main + with: + determinate: true + - uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16 + with: + name: ghostty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + skipPush: true + useDaemon: false # sometimes fails on short jobs + + - name: swiftlint check + run: nix develop -c swiftlint lint --strict macos + alejandra: if: github.repository == 'ghostty-org/ghostty' runs-on: namespace-profile-ghostty-xsm diff --git a/AGENTS.md b/AGENTS.md index 04d3570a7..949bf588e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,6 +8,7 @@ A file for [guiding coding agents](https://agents.md/). - **Test (Zig):** `zig build test` - **Test filter (Zig)**: `zig build test -Dtest-filter=` - **Formatting (Zig)**: `zig fmt .` +- **Formatting (Swift)**: `swiftlint lint --fix macos` - **Formatting (other)**: `prettier -w .` ## Directory Structure diff --git a/HACKING.md b/HACKING.md index 921ed71ff..23657cea5 100644 --- a/HACKING.md +++ b/HACKING.md @@ -186,6 +186,31 @@ shellcheck \ $(find . \( -name "*.sh" -o -name "*.bash" \) -type f ! -path "./zig-out/*" ! -path "./macos/build/*" ! -path "./.git/*" | sort) ``` +### SwiftLint + +Swift code is linted using [SwiftLint](https://github.com/realm/SwiftLint). A +SwiftLint CI check will fail builds with improper formatting. Therefore, if you +are modifying Swift code, you may want to install it locally and run this from +the repo root before you commit: + +``` +swiftlint lint --fix macos +``` + +Make sure your SwiftLint version matches the version in [devShell.nix](https://github.com/ghostty-org/ghostty/blob/main/nix/devShell.nix). + +Nix users can use the following command to format with SwiftLint: + +``` +nix develop -c swiftlint lint --fix macos +``` + +To check for violations without auto-fixing: + +``` +nix develop -c swiftlint lint --strict macos +``` + ### Updating the Zig Cache Fixed-Output Derivation Hash The Nix package depends on a [fixed-output diff --git a/macos/.swiftlint.yml b/macos/.swiftlint.yml new file mode 100644 index 000000000..e9767a43f --- /dev/null +++ b/macos/.swiftlint.yml @@ -0,0 +1,64 @@ +# SwiftLint +# +check_for_updates: false + +disabled_rules: + - cyclomatic_complexity + - file_length + - function_body_length + - nesting + - todo + - trailing_comma + - trailing_newline + - type_body_length + + # TODO + - colon + - comma + - comment_spacing + - control_statement + - deployment_target + - empty_enum_arguments + - empty_parentheses_with_trailing_closure + - for_where + - force_cast + - implicit_getter + - implicit_optional_initialization + - legacy_constant + - legacy_constructor + - line_length + - mark + - multiple_closures_with_trailing_closure + - no_fallthrough_only + - opening_brace + - orphaned_doc_comment + - private_over_fileprivate + - shorthand_operator + - switch_case_alignment + - syntactic_sugar + - trailing_semicolon + - trailing_whitespace + - unneeded_synthesized_initializer + - unused_closure_parameter + - unused_enumerated + - unused_optional_binding + - vertical_parameter_alignment + - vertical_whitespace + +identifier_name: + min_length: 1 + allowed_symbols: ["_"] + excluded: + - Core.* + +type_name: + min_length: 2 + allowed_symbols: ["_"] + excluded: + - iOS_.* + +function_parameter_count: + warning: 6 + +large_tuple: + warning: 3 diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index ab6dde118..e69331367 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -355,6 +355,7 @@ isa = PBXNativeTarget; buildConfigurationList = A5B30540299BEAAB0047F10C /* Build configuration list for PBXNativeTarget "Ghostty" */; buildPhases = ( + FC501E0B2F46B410007AE49D /* Run SwiftLint */, A5B3052D299BEAAA0047F10C /* Sources */, A5B3052E299BEAAA0047F10C /* Frameworks */, A5B3052F299BEAAA0047F10C /* Resources */, @@ -490,6 +491,29 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + FC501E0B2F46B410007AE49D /* Run SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run SwiftLint"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "[[ -z \"$GITHUB_ACTIONS\" ]] || exit 0;\n\nSWIFTLINT=\"\"\nif command -v swiftlint >/dev/null 2>&1; then\n SWIFTLINT=\"$(command -v swiftlint)\"\nelif [[ -f \"/opt/homebrew/bin/swiftlint\" ]]; then\n SWIFTLINT=\"/opt/homebrew/bin/swiftlint\"\nfi\n\nif [[ -n \"$SWIFTLINT\" ]]; then\n \"$SWIFTLINT\" lint --quiet \"$SRCROOT\"\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 810ACC9B2E9D3301004F8F92 /* Sources */ = { isa = PBXSourcesBuildPhase; diff --git a/nix/devShell.nix b/nix/devShell.nix index 90059a730..c78c9081b 100644 --- a/nix/devShell.nix +++ b/nix/devShell.nix @@ -66,6 +66,7 @@ poop, typos, shellcheck, + swiftlint, uv, wayland, wayland-scanner, @@ -198,6 +199,9 @@ in # for benchmarking poop + ] + ++ lib.optionals stdenv.hostPlatform.isDarwin [ + swiftlint ]; # This should be set onto the rpath of the ghostty binary if you want