27

So far what I found is this blog article: It's time to use Swift Package Manager which recommends integrate SwiftLint and other tools with Package.swift.

I was able to add dependency to the package file, build and test successfully but SwiftLint never warns me about syntax violations.

Before we used this Build Phases step in Xcode project:

if which swiftlint >/dev/null; then
swiftlint
else
echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
fi

The article suggests adopting Komondor which itself useless without PackageConfigs. The idea is to run SwiftLint command during commit. I have tried to add both projects and couldn't get it working in reasonable amount of time. During commit, I see warnings like this:

Illegal instruction: 4 $komondor run pre-commit

This is still early days for Swift Package Manager and there's almost no information on the internet.

Ideally I would like to have any solution which allows our team to automate SwiftLint, and ideally that wouldn't require adding 22 dependencies, config files, and require dynamic library.

Boris Y.
  • 4,387
  • 2
  • 32
  • 50

3 Answers3

7

You can now officially use SwiftLint via a Swift Package Manager plugin. To do this, add the package dependency and then specify the plugin in the target you want to lint in Package.swift:

let package = Package(
    ...
    dependencies: [
        .package(url: "https://github.com/realm/SwiftLint", branch: "main")
    ],
    targets: [
        .target(
            ...
            plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")]
        )
    ]
)

As of 2023-01-13, I found that specifying an exact version of the latest release (0.50.3) in the package dependency causes a SPM failure to resolve dependencies. Using either the branch set to main (as above) or a specific revision both seem to work.

Then, in .swiftlint.yml, you can specify the directories to lint:

included:
  - Sources
  - Tests
Josh Brown
  • 52,385
  • 10
  • 54
  • 80
  • How did you specify the files/directories to lint? For me, the command generated with linting the only file inside the my local swift package. I tried to fill in directories under `included` inside `.swiftlint.yml`, but no luck. Thanks – azun Jan 19 '23 at 03:37
  • @KhangAzun I believe this all worked for me without a `.swiftlint.yml` (using the default rules) but I wanted to only lint files in Sources and Tests, so I added those to the `included` in `.swiftlint.yml`. Updated my answer to include this. If that's not working for you, I'm not sure what's going on there. – Josh Brown Jan 19 '23 at 15:37
  • Thanks Josh. I have the same result as yours. Sorry that I didn't state that. However, I wanted to run lint for files outside of the package's content. Then I followed @Filozoff above, and was able to specify the directory to lint with a relative path (via '--path' parameter) – azun Jan 21 '23 at 03:29
  • @JoshBrown where did you place the `.swiftlint.yml` file? I have tried * in the root of the project. * in the sources folder for the package. * in the root for the package. For testing purpose I have tried to specify some disabled_rules but none of them applies. – martinmose Feb 02 '23 at 16:06
  • 1
    @martinmose I have it in the root of the repository, right next to Package.swift. – Josh Brown Mar 03 '23 at 19:46
6

A proposal in Swift, called Package Manager Extensible Build Tools (SE-0303) has been implemented, and is available in Swift 5.6, which is not yet released, as of October 2021.

A new target type, plugin (alongside executable and library) will be available to configure commands during the build. This means SwiftLint has some work to be a Swift package plugin. The SwiftLint authors could implement a package plugin that creates a command to run Swiftlint before the build.


I'll update when this proposal is implemented, and also when SwiftLint supports Extensible Build Tools

Ben Butterworth
  • 22,056
  • 10
  • 114
  • 167
6

It is possible to attach SwiftLint to Swift Package since version 0.47.1. You can use .artifactbundle.zip artefact: In your Package.swift:

(...)
targets: [
    .binaryTarget(
        name: "SwiftLintBinary",
        url: "https://github.com/realm/SwiftLint/releases/download/0.47.1/SwiftLintBinary-macos.artifactbundle.zip",
        checksum: "cdc36c26225fba80efc3ac2e67c2e3c3f54937145869ea5dbcaa234e57fc3724"
    ),
    .plugin(
        name: "SwiftLintPlugin",
        capability: .buildTool(),
        dependencies: ["SwiftLintBinary"]
    ),
    .target(
        name: "YourPackageName",
        dependencies: [],
        plugins: ["SwiftLintPlugin"]
    ),
    (...)
]

Next you have to set up a plugin configuration. In your "Plugins/SwiftLintPlugin" directory create a "plugin.swift" file:

import PackagePlugin

@main
struct SwiftLintPlugins: BuildToolPlugin {
    
    func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
        return [
            .buildCommand(
                displayName: "Linting \(target.name)...",
                executable: try context.tool(named: "swiftlint").path,
                arguments: [
                    "lint",
                    "--in-process-sourcekit",
                    "--path",
                    target.directory.string   // only lint the files in the target directory
                ],
                environment: [:]
            )
        ]
    }
}

And that's it.

If you need more context how it is done, you can also check out my repository here: https://github.com/Filozoff/Kukulka

Filozoff
  • 79
  • 1
  • 1
  • This is a good solution - is there any to target the swiftlint artifact bundle in a way that doesn't require manually updating the version number? – user3062913 Jun 23 '22 at 11:33
  • Where did you find the value for the checksum when adding the binary target? I've upped the version to 0.49.1 and am seeing this error: "checksum of downloaded artifact of binary target 'SwiftLintBinary' (227258fdb2f920f8ce90d4f08d019e1b0db5a4ad2090afa012fd7c2c91716df3) does not match checksum specified by the manifest (cdc36c26225fba80efc3ac2e67c2e3c3f54937145869ea5dbcaa234e57fc3724)" – Jake Strickler Sep 13 '22 at 19:53
  • @JakeStrickler there's a better way to use the SwifLint plugin now -- [see my answer below](https://stackoverflow.com/a/75110849/2030). – Josh Brown Jan 19 '23 at 15:43