├── .github
├── FUNDING.yml
├── stale.yml
└── workflows
│ └── update-swiftlint.yml
├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── LICENSE
├── Package.swift
├── Plugins
├── SwiftLint
│ └── main.swift
└── SwiftLintFix
│ └── main.swift
└── README.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: lukepistrol # Replace with Github sponsors username
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with open collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: ['buymeacoffee.com/lukeeep'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 20
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | # Label to use when marking an issue as stale
10 | staleLabel: wontfix
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had
14 | recent activity. It will be closed if no further activity occurs. Thank you
15 | for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: false
18 |
--------------------------------------------------------------------------------
/.github/workflows/update-swiftlint.yml:
--------------------------------------------------------------------------------
1 | name: Update SwiftLint
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * 1' # Runs every Monday at midnight
6 | workflow_dispatch:
7 |
8 | jobs:
9 | update-swiftlint:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout repository
14 | uses: actions/checkout@v4
15 |
16 | - name: Get latest SwiftLint release
17 | id: swiftlint_release
18 | run: |
19 | latest_release=$(curl --silent "https://api.github.com/repos/realm/SwiftLint/releases/latest" | jq -r '.tag_name')
20 | echo "Latest release: $latest_release"
21 | echo "::set-output name=latest_release::$latest_release"
22 |
23 | - name: Fetch SwiftLint binary URL and checksum
24 | id: fetch_details
25 | run: |
26 | latest_release=${{ steps.swiftlint_release.outputs.latest_release }}
27 | binary_url="https://github.com/realm/SwiftLint/releases/download/${latest_release}/SwiftLintBinary.artifactbundle.zip"
28 | checksum=$(curl -L $binary_url | shasum -a 256 | awk '{ print $1 }')
29 | echo "Binary URL: $binary_url"
30 | echo "Checksum: $checksum"
31 | echo "::set-output name=binary_url::$binary_url"
32 | echo "::set-output name=checksum::$checksum"
33 |
34 | - name: Update Swift Package
35 | run: |
36 | binary_url=${{ steps.fetch_details.outputs.binary_url }}
37 | checksum=${{ steps.fetch_details.outputs.checksum }}
38 | sed -i 's|url: "https://github.com/realm/SwiftLint/releases/download/.*"|url: "'"${binary_url}"'"|' Package.swift
39 | sed -i 's|checksum: ".*"|checksum: "'"${checksum}"'"|' Package.swift
40 |
41 | - name: Check for changes
42 | id: check_changes
43 | run: |
44 | if git diff --quiet; then
45 | echo "No changes detected"
46 | echo "::set-output name=changes_detected::false"
47 | else
48 | echo "Changes detected"
49 | echo "::set-output name=changes_detected::true"
50 | fi
51 |
52 | - name: Commit and push changes
53 | if: steps.check_changes.outputs.changes_detected == 'true'
54 | run: |
55 | git config --global user.name 'github-actions[bot]'
56 | git config --global user.email 'github-actions[bot]@users.noreply.github.com'
57 | git add Package.swift
58 | git commit -m "Update SwiftLint to ${{ steps.swiftlint_release.outputs.latest_release }}"
59 | git push
60 | env:
61 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Lukas Pistrol
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.7
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "SwiftLintPlugin",
7 | platforms: [
8 | .iOS(.v13),
9 | .watchOS(.v6),
10 | .macOS(.v10_15),
11 | .tvOS(.v13),
12 | ],
13 | products: [
14 | .plugin(
15 | name: "SwiftLint",
16 | targets: ["SwiftLint"]
17 | ),
18 | .plugin(
19 | name: "SwiftLintFix",
20 | targets: ["SwiftLintFix"]
21 | ),
22 | ],
23 | targets: [
24 | .binaryTarget(
25 | name: "SwiftLintBinary",
26 | url: "https://github.com/realm/SwiftLint/releases/download/0.59.1/SwiftLintBinary.artifactbundle.zip",
27 | checksum: "b9f915a58a818afcc66846740d272d5e73f37baf874e7809ff6f246ea98ad8a2"
28 | ),
29 |
30 | .plugin(
31 | name: "SwiftLint",
32 | capability: .buildTool(),
33 | dependencies: ["SwiftLintBinary"]
34 | ),
35 |
36 | .plugin(
37 | name: "SwiftLintFix",
38 | capability: .command(
39 | intent: .sourceCodeFormatting(),
40 | permissions: [.writeToPackageDirectory(reason: "Fixes fixable lint issues")]
41 | ),
42 | dependencies: ["SwiftLintBinary"]
43 | ),
44 | ]
45 | )
46 |
--------------------------------------------------------------------------------
/Plugins/SwiftLint/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // Plugins/SwiftLint
4 | //
5 | // Created by Lukas Pistrol on 23.06.22.
6 | //
7 | // Big thanks to @marcoboerner on GitHub
8 | // https://github.com/realm/SwiftLint/issues/3840#issuecomment-1085699163
9 | //
10 |
11 | import Foundation
12 | import PackagePlugin
13 |
14 | @main
15 | struct SwiftLintPlugin: BuildToolPlugin {
16 | func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
17 | return buildCommands(
18 | workingDirectory: context.pluginWorkDirectory,
19 | packageDirectory: context.package.directory,
20 | targetDirectory: target.directory,
21 | tool: try context.tool(named: "swiftlint")
22 | )
23 | }
24 |
25 | private func buildCommands(
26 | workingDirectory: Path,
27 | packageDirectory: Path,
28 | targetDirectory: Path,
29 | tool: PluginContext.Tool
30 | ) -> [Command] {
31 | // If the DISABLE_SWIFTLINT environment variable is set, don't run SwiftLint.
32 | if ProcessInfo.processInfo.environment["DISABLE_SWIFTLINT"] != nil {
33 | return []
34 | }
35 |
36 | var arguments = [
37 | "lint",
38 | "--config",
39 | "\(packageDirectory.string)/.swiftlint.yml",
40 | ]
41 |
42 | // If running in Xcode Cloud, disable caching.
43 | if ProcessInfo.processInfo.environment["CI_XCODE_CLOUD"] == "TRUE" {
44 | arguments.append("--no-cache")
45 | } else {
46 | arguments.append("--cache-path")
47 | arguments.append("\(workingDirectory)")
48 | }
49 |
50 | arguments.append(targetDirectory.string)
51 |
52 | return [
53 | .prebuildCommand(
54 | displayName: "Running SwiftLint for \(targetDirectory.lastComponent)",
55 | executable: tool.path,
56 | arguments: arguments,
57 | outputFilesDirectory: workingDirectory.appending("Output")
58 | )
59 | ]
60 | }
61 | }
62 |
63 | #if canImport(XcodeProjectPlugin)
64 | import XcodeProjectPlugin
65 |
66 | extension SwiftLintPlugin: XcodeBuildToolPlugin {
67 | func createBuildCommands(context: XcodePluginContext, target: XcodeTarget) throws -> [Command] {
68 | return buildCommands(
69 | workingDirectory: context.pluginWorkDirectory,
70 | packageDirectory: context.xcodeProject.directory,
71 | targetDirectory: context.xcodeProject.directory,
72 | tool: try context.tool(named: "swiftlint")
73 | )
74 | }
75 | }
76 | #endif
77 |
--------------------------------------------------------------------------------
/Plugins/SwiftLintFix/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Lukas Pistrol on 31.10.22.
6 | //
7 |
8 | import PackagePlugin
9 | import Foundation
10 |
11 | @main
12 | struct MyCommandPlugin: CommandPlugin {
13 |
14 | func performCommand(context: PluginContext, arguments: [String]) throws {
15 | let tool = try context.tool(named: "swiftlint")
16 | let toolUrl = URL(fileURLWithPath: tool.path.string)
17 |
18 | for target in context.package.targets {
19 | guard let target = target as? SourceModuleTarget else { continue }
20 |
21 | let process = Process()
22 | process.executableURL = toolUrl
23 | process.arguments = [
24 | "--fix",
25 | "--config",
26 | "\(context.package.directory.string)/.swiftlint.yml",
27 | "\(target.directory)"
28 | ]
29 |
30 | print(toolUrl.path, process.arguments!.joined(separator: " "))
31 |
32 | try process.run()
33 | process.waitUntilExit()
34 |
35 | if process.terminationReason == .exit && process.terminationStatus == 0 {
36 | print("Formatted the source code in \(target.directory).")
37 | } else {
38 | let problem = "\(process.terminationReason):\(process.terminationStatus)"
39 | Diagnostics.error("swift-format invocation failed: \(problem)")
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftLintPlugin
2 |
3 | [](https://swiftpackageindex.com/lukepistrol/SwiftLintPlugin)
4 | [](https://swiftpackageindex.com/lukepistrol/SwiftLintPlugin)
5 | [](https://github.com/lukepistrol/SwiftLintPlugin/blob/main/LICENSE)
6 | [](https://twitter.com/lukeeep_)
7 |
8 | A Swift Package Plugin for [SwiftLint](https://github.com/realm/SwiftLint/) that will run SwiftLint on build time and show errors & warnings in Xcode.
9 |
10 | > **Note**
11 | > There now is an official version in the [SwiftLint repo](https://github.com/realm/SwiftLint#plug-in-support)!
12 | > Though this package will still be maintained and updated since it brings the benefit of being a smaller repository and
13 | > therefore faster to download as a dependency
14 |
15 | ## Add to Package
16 |
17 | First add a dependency from this package:
18 |
19 | ```swift
20 | dependencies: [
21 | // ...
22 | .package(url: "https://github.com/lukepistrol/SwiftLintPlugin", from: "0.2.2"),
23 | ]
24 | ```
25 |
26 | Then add it to your targets as a plugin:
27 |
28 | ```swift
29 | targets: [
30 | .target(
31 | name: "YOUR_TARGET",
32 | dependencies: [],
33 | plugins: [
34 | .plugin(name: "SwiftLint", package: "SwiftLintPlugin")
35 | ]
36 | ),
37 | ]
38 | ```
39 |
40 | ## Add to Project
41 |
42 | Starting with Xcode 14, plugins can also work on Xcode Project's targets. To do so, simply add this package to your SPM dependencies in Xcode. After that open your `target's settings > Build Phases` and add `SwiftLint` to `Run Build Tool Plug-ins` like shown below:
43 |
44 |
45 |
46 | > You may need to enable & trust the plugin before you can actually run it during builds.
47 |
48 | ## Fix Warnings
49 |
50 | As of version `0.1.0` this package also includes a command plugin which can be called on any target.
51 |
52 | 1. Select a project or package in the project navigator.
53 | 2. Richt-click and select `SwiftLintFix`.
54 | - alternatively you can select `File > Packages > SwiftLintFix`.
55 | 3. Choose the target(s) to run the `swiftlint --fix` command on.
56 |
57 |
58 |
59 | ## Run on CI
60 |
61 | Important to notice is that when building a package/project on any CI provider (e.g. GitHub Actions) it is mandatory to pass the `-skipPackagePluginValidation` flag to the `xcodebuild` command. This will skip the validation prompt which in Xcode looks like this:
62 |
63 |
64 |
65 | ### Example
66 |
67 | ```bash
68 | xcodebuild \
69 | -scheme "$SCHEME" \
70 | -destination "$PLATFORM" \
71 | -skipPackagePluginValidation \ # this is mandatory
72 | clean build
73 | ```
74 |
75 | If you need to disable linting (for release/app store builds), you can set`DISABLE_SWIFTLINT` environment variable
76 |
77 | -----
78 |
79 |
80 |
--------------------------------------------------------------------------------