diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b78476e6..f387d66e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,6 +58,14 @@ jobs: install: true cache: true + - name: Install SwiftLint + shell: bash + run: | + set -euo pipefail + + brew list swiftlint >/dev/null 2>&1 || brew install swiftlint + swiftlint version + - name: Cache SwiftPM uses: actions/cache@v5 with: @@ -255,6 +263,14 @@ jobs: install: true cache: true + - name: Install SwiftLint + shell: bash + run: | + set -euo pipefail + + brew list swiftlint >/dev/null 2>&1 || brew install swiftlint + swiftlint version + - name: Cache SwiftPM uses: actions/cache@v5 with: diff --git a/.github/workflows/testflight.yml b/.github/workflows/testflight.yml index 200053df..e3304ad0 100644 --- a/.github/workflows/testflight.yml +++ b/.github/workflows/testflight.yml @@ -90,6 +90,14 @@ jobs: install: true cache: true + - name: Install SwiftLint + shell: bash + run: | + set -euo pipefail + + brew list swiftlint >/dev/null 2>&1 || brew install swiftlint + swiftlint version + - name: Generate Xcode workspace with Tuist shell: bash run: | diff --git a/Application/DevLogApp/Project.swift b/Application/DevLogApp/Project.swift index fa3d73c7..992d31d7 100644 --- a/Application/DevLogApp/Project.swift +++ b/Application/DevLogApp/Project.swift @@ -7,7 +7,7 @@ let project = Project( disableBundleAccessors: true, disableSynthesizedResourceAccessors: true ), - packages: DevLogPackages.lintOnlyPackages, + packages: DevLogPackages.defaultPackages, settings: .devlogProject(versionXcconfigPath: "../Shared/Version.xcconfig"), targets: [ .target( @@ -24,6 +24,12 @@ let project = Project( "Sources/Resource/Localizable.xcstrings", ], entitlements: .file(path: "Sources/Resource/DevLog.entitlements"), + scripts: [ + DevLogScripts.swiftLint( + sourcePath: "Sources", + configPath: "Sources/.swiftlint.yml" + ), + ], dependencies: [ .project(target: "DevLogPresentation", path: "../DevLogPresentation"), .project(target: "DevLogPersistence", path: "../DevLogPersistence"), @@ -33,13 +39,13 @@ let project = Project( .project(target: "DevLogCore", path: "../DevLogCore"), .project(target: "DevLogWidgetCore", path: "../../Widget/DevLogWidgetCore"), .project(target: "DevLogWidgetExtension", path: "../../Widget/DevLogWidgetExtension"), - DevLogPackages.swiftLintPlugin, ], settings: .devlog( versionXcconfigPath: "Sources/Resource/App.xcconfig", base: [ "ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon", "CODE_SIGN_STYLE": "Automatic", + "ENABLE_USER_SCRIPT_SANDBOXING": "NO", "PRODUCT_MODULE_NAME": "DevLogApp", ], debug: [ @@ -57,6 +63,12 @@ let project = Project( bundleId: "opfic.DevLogAppTests", infoPlist: .file(path: "../Shared/InfoPlists/UnitTests-Info.plist"), sources: ["Tests/**/*.swift"], + scripts: [ + DevLogScripts.swiftLint( + sourcePath: "Tests", + configPath: "Tests/.swiftlint.yml" + ), + ], dependencies: [ .target(name: "DevLogApp"), ], @@ -64,6 +76,7 @@ let project = Project( base: [ "BUNDLE_LOADER": "$(TEST_HOST)", "CODE_SIGN_STYLE": "Automatic", + "ENABLE_USER_SCRIPT_SANDBOXING": "NO", "TEST_HOST": "$(BUILT_PRODUCTS_DIR)/DevLog.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/DevLog", "TEST_TARGET_NAME": "DevLogApp", ] diff --git a/Application/DevLogCore/Project.swift b/Application/DevLogCore/Project.swift index 8e24b929..df39509e 100644 --- a/Application/DevLogCore/Project.swift +++ b/Application/DevLogCore/Project.swift @@ -7,6 +7,6 @@ let project = Project.devlogFramework( versionXcconfigPath: "../Shared/Version.xcconfig", frameworkInfoPlistPath: "../Shared/InfoPlists/Framework-Info.plist", testsInfoPlistPath: "../Shared/InfoPlists/UnitTests-Info.plist", - packages: DevLogPackages.lintOnlyPackages, + packages: DevLogPackages.defaultPackages, hasTests: false ) diff --git a/Application/DevLogData/Project.swift b/Application/DevLogData/Project.swift index e43b053f..ca3bd22e 100644 --- a/Application/DevLogData/Project.swift +++ b/Application/DevLogData/Project.swift @@ -7,7 +7,7 @@ let project = Project.devlogFramework( versionXcconfigPath: "../Shared/Version.xcconfig", frameworkInfoPlistPath: "../Shared/InfoPlists/Framework-Info.plist", testsInfoPlistPath: "../Shared/InfoPlists/UnitTests-Info.plist", - packages: DevLogPackages.lintOnlyPackages, + packages: DevLogPackages.defaultPackages, dependencies: [ .project(target: "DevLogDomain", path: "../DevLogDomain"), .project(target: "DevLogCore", path: "../DevLogCore"), diff --git a/Application/DevLogDomain/Project.swift b/Application/DevLogDomain/Project.swift index 305c68e4..b029fb11 100644 --- a/Application/DevLogDomain/Project.swift +++ b/Application/DevLogDomain/Project.swift @@ -7,7 +7,7 @@ let project = Project.devlogFramework( versionXcconfigPath: "../Shared/Version.xcconfig", frameworkInfoPlistPath: "../Shared/InfoPlists/Framework-Info.plist", testsInfoPlistPath: "../Shared/InfoPlists/UnitTests-Info.plist", - packages: DevLogPackages.lintOnlyPackages, + packages: DevLogPackages.defaultPackages, dependencies: [ .project(target: "DevLogCore", path: "../DevLogCore"), ], diff --git a/Application/DevLogPersistence/Project.swift b/Application/DevLogPersistence/Project.swift index 9e2403a8..f73516b6 100644 --- a/Application/DevLogPersistence/Project.swift +++ b/Application/DevLogPersistence/Project.swift @@ -7,7 +7,7 @@ let project = Project.devlogFramework( versionXcconfigPath: "../Shared/Version.xcconfig", frameworkInfoPlistPath: "../Shared/InfoPlists/Framework-Info.plist", testsInfoPlistPath: "../Shared/InfoPlists/UnitTests-Info.plist", - packages: DevLogPackages.lintOnlyPackages, + packages: DevLogPackages.defaultPackages, dependencies: [ .project(target: "DevLogData", path: "../DevLogData"), .project(target: "DevLogCore", path: "../DevLogCore"), diff --git a/README.md b/README.md index 5db0b3ed..0942f268 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ MVVM을 기반으로 하되, ViewModel 상태 관리에는 MVI 형태의 단방 ```bash brew install mise +brew install swiftlint mise install ``` diff --git a/Tuist/ProjectDescriptionHelpers/Project+Packages.swift b/Tuist/ProjectDescriptionHelpers/Project+Packages.swift index f1ed88e4..803fe537 100644 --- a/Tuist/ProjectDescriptionHelpers/Project+Packages.swift +++ b/Tuist/ProjectDescriptionHelpers/Project+Packages.swift @@ -1,10 +1,6 @@ import ProjectDescription public enum DevLogPackages { - public static let swiftLintPackage: Package = .package( - url: "https://github.com/realm/SwiftLint", - .upToNextMajor(from: "0.62.1") - ) public static let markdownUIPackage: Package = .package( url: "https://github.com/gonzalezreal/swift-markdown-ui.git", .upToNextMajor(from: "2.4.1") @@ -13,6 +9,10 @@ public enum DevLogPackages { url: "https://github.com/apple/swift-collections.git", .upToNextMajor(from: "1.3.0") ) + public static let composableArchitecturePackage: Package = .package( + url: "https://github.com/pointfreeco/swift-composable-architecture", + .upToNextMajor(from: "1.25.5") + ) public static let firebasePackage: Package = .package( url: "https://github.com/firebase/firebase-ios-sdk", .upToNextMajor(from: "11.15.0") @@ -26,12 +26,8 @@ public enum DevLogPackages { .upToNextMajor(from: "1.1.0") ) - public static let swiftLintPlugin: TargetDependency = .package( - product: "SwiftLintBuildToolPlugin", - type: .plugin - ) - public static let presentationPackageDependencies: [TargetDependency] = [ + .package(product: "ComposableArchitecture"), .package(product: "MarkdownUI"), .package(product: "OrderedCollections"), ] @@ -47,20 +43,62 @@ public enum DevLogPackages { .package(product: "Nexa"), ] - public static let lintOnlyPackages: [Package] = [ - swiftLintPackage, - ] + public static let defaultPackages: [Package] = [] public static let presentationPackages: [Package] = [ - swiftLintPackage, + composableArchitecturePackage, markdownUIPackage, swiftCollectionsPackage, ] public static let infraPackages: [Package] = [ - swiftLintPackage, firebasePackage, googleSignInPackage, nexaPackage, ] } + +public enum DevLogScripts { + public static func swiftLint( + sourcePath: String, + configPath: String = "../../.swiftlint.yml" + ) -> TargetScript { + TargetScript.pre( + script: """ + export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH" + + swiftLintPath="$(command -v swiftlint || true)" + if [ -z "$swiftLintPath" ]; then + echo "error: SwiftLint is not installed. Run 'brew install swiftlint'." + exit 1 + fi + + configPath="${SRCROOT}/\(configPath)" + sourcePathName="\(sourcePath)" + lintSourcePath="${SRCROOT}/${sourcePathName}" + + if [ "$sourcePathName" != "." ]; then + "$swiftLintPath" lint --config "$configPath" "$lintSourcePath" + else + swiftFilePaths=() + while IFS= read -r -d '' swiftFilePath; do + swiftFilePaths+=("$swiftFilePath") + done < <(find "$lintSourcePath" -name "*.swift" -not -path "*/Derived/*" -not -name "Project.swift" -print0) + + if [ ${#swiftFilePaths[@]} -lt 1 ]; then + exit 0 + fi + + "$swiftLintPath" lint --config "$configPath" "${swiftFilePaths[@]}" + fi + """, + name: "SwiftLint", + inputPaths: [ + "$(SRCROOT)/\(configPath)", + "$(SRCROOT)/\(sourcePath)", + ], + basedOnDependencyAnalysis: false, + shellPath: "/bin/bash" + ) + } +} diff --git a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift index 8e7391a7..f6a53760 100644 --- a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift +++ b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift @@ -7,7 +7,7 @@ public extension Project { versionXcconfigPath: Path, frameworkInfoPlistPath: Path, testsInfoPlistPath: Path, - packages: [Package] = DevLogPackages.lintOnlyPackages, + packages: [Package] = DevLogPackages.defaultPackages, dependencies: [TargetDependency] = [], hasTests: Bool ) -> Project { @@ -19,8 +19,19 @@ public extension Project { bundleId: bundleId, infoPlist: .file(path: frameworkInfoPlistPath), sources: ["Sources/**/*.swift"], - dependencies: dependencies + [DevLogPackages.swiftLintPlugin], - settings: .devlog(versionXcconfigPath: versionXcconfigPath) + scripts: [ + DevLogScripts.swiftLint( + sourcePath: "Sources", + configPath: "Sources/.swiftlint.yml" + ), + ], + dependencies: dependencies, + settings: .devlog( + versionXcconfigPath: versionXcconfigPath, + base: [ + "ENABLE_USER_SCRIPT_SANDBOXING": "NO", + ] + ) ), ] @@ -33,11 +44,18 @@ public extension Project { bundleId: "\(bundleId)Tests", infoPlist: .file(path: testsInfoPlistPath), sources: ["Tests/**/*.swift"], + scripts: [ + DevLogScripts.swiftLint( + sourcePath: "Tests", + configPath: "Tests/.swiftlint.yml" + ), + ], dependencies: [ .target(name: name), ], settings: .devlog( base: [ + "ENABLE_USER_SCRIPT_SANDBOXING": "NO", "TEST_TARGET_NAME": SettingValue(stringLiteral: name), ] ) diff --git a/Widget/DevLogWidgetCore/Project.swift b/Widget/DevLogWidgetCore/Project.swift index 8da2be34..584b74f8 100644 --- a/Widget/DevLogWidgetCore/Project.swift +++ b/Widget/DevLogWidgetCore/Project.swift @@ -7,7 +7,7 @@ let project = Project.devlogFramework( versionXcconfigPath: "../../Application/Shared/Version.xcconfig", frameworkInfoPlistPath: "../../Application/Shared/InfoPlists/Framework-Info.plist", testsInfoPlistPath: "../../Application/Shared/InfoPlists/UnitTests-Info.plist", - packages: DevLogPackages.lintOnlyPackages, + packages: DevLogPackages.defaultPackages, dependencies: [ .project(target: "DevLogCore", path: "../../Application/DevLogCore"), ], diff --git a/Widget/DevLogWidgetExtension/Project.swift b/Widget/DevLogWidgetExtension/Project.swift index 77be6ff9..f977f2dc 100644 --- a/Widget/DevLogWidgetExtension/Project.swift +++ b/Widget/DevLogWidgetExtension/Project.swift @@ -29,6 +29,9 @@ let project = Project( "Resource/Localizable.xcstrings", ], entitlements: .file(path: "Resource/DevLogWidget.entitlements"), + scripts: [ + DevLogScripts.swiftLint(sourcePath: "."), + ], dependencies: [ .project(target: "DevLogWidgetCore", path: "../DevLogWidgetCore"), ], @@ -36,6 +39,7 @@ let project = Project( versionXcconfigPath: "../../Application/Shared/Version.xcconfig", base: [ "CODE_SIGN_STYLE": "Automatic", + "ENABLE_USER_SCRIPT_SANDBOXING": "NO", ] ) ),