From 43b34b7e6bedeed469a13d09d551e05e0ffba752 Mon Sep 17 00:00:00 2001 From: opficdev <162981733+opficdev@users.noreply.github.com> Date: Thu, 4 Jun 2026 23:26:34 +0900 Subject: [PATCH 1/4] =?UTF-8?q?chore:=20SwiftLint=20=EC=8B=A4=ED=96=89=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 16 ++++++ .github/workflows/testflight.yml | 8 +++ Application/DevLogApp/Project.swift | 7 ++- Application/DevLogCore/Project.swift | 2 +- Application/DevLogData/Project.swift | 2 +- Application/DevLogDomain/Project.swift | 2 +- Application/DevLogPersistence/Project.swift | 2 +- README.md | 1 + .../Project+Packages.swift | 52 ++++++++++++++----- .../Project+Templates.swift | 14 +++-- Widget/DevLogWidgetCore/Project.swift | 2 +- Widget/DevLogWidgetExtension/Project.swift | 4 ++ 12 files changed, 88 insertions(+), 24 deletions(-) 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..2d0053b6 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,9 @@ let project = Project( "Sources/Resource/Localizable.xcstrings", ], entitlements: .file(path: "Sources/Resource/DevLog.entitlements"), + scripts: [ + DevLogScripts.swiftLint(sourcePath: "Sources"), + ], dependencies: [ .project(target: "DevLogPresentation", path: "../DevLogPresentation"), .project(target: "DevLogPersistence", path: "../DevLogPersistence"), @@ -33,13 +36,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: [ 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..25c32671 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") @@ -26,11 +22,6 @@ 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: "MarkdownUI"), .package(product: "OrderedCollections"), @@ -47,20 +38,53 @@ public enum DevLogPackages { .package(product: "Nexa"), ] - public static let lintOnlyPackages: [Package] = [ - swiftLintPackage, - ] + public static let defaultPackages: [Package] = [] public static let presentationPackages: [Package] = [ - swiftLintPackage, markdownUIPackage, swiftCollectionsPackage, ] public static let infraPackages: [Package] = [ - swiftLintPackage, firebasePackage, googleSignInPackage, nexaPackage, ] } + +public enum DevLogScripts { + public static func swiftLint(sourcePath: String) -> 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}/../../.swiftlint.yml" + sourcePathName="\(sourcePath)" + lintSourcePath="${SRCROOT}/${sourcePathName}" + + if [ "$sourcePathName" != "." ]; then + "$swiftLintPath" lint --config "$configPath" "$lintSourcePath" + else + status=0 + while IFS= read -r swiftFilePath; do + "$swiftLintPath" lint --config "$configPath" "$swiftFilePath" || status=$? + done < <(find "$lintSourcePath" -name "*.swift" -not -path "*/Derived/*" -not -name "Project.swift") + exit "$status" + fi + """, + name: "SwiftLint", + inputPaths: [ + "$(SRCROOT)/../../.swiftlint.yml", + "$(SRCROOT)/\(sourcePath)", + ], + basedOnDependencyAnalysis: false, + shellPath: "/bin/bash" + ) + } +} diff --git a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift index 8e7391a7..4642acb5 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,16 @@ 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"), + ], + dependencies: dependencies, + settings: .devlog( + versionXcconfigPath: versionXcconfigPath, + base: [ + "ENABLE_USER_SCRIPT_SANDBOXING": "NO", + ] + ) ), ] 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", ] ) ), From cc247e696871d64361f19989421d7ec7bbc7e61a Mon Sep 17 00:00:00 2001 From: opficdev <162981733+opficdev@users.noreply.github.com> Date: Thu, 4 Jun 2026 23:27:08 +0900 Subject: [PATCH 2/4] =?UTF-8?q?chore:=20TCA=20=EC=9D=98=EC=A1=B4=EC=84=B1?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Tuist/ProjectDescriptionHelpers/Project+Packages.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Tuist/ProjectDescriptionHelpers/Project+Packages.swift b/Tuist/ProjectDescriptionHelpers/Project+Packages.swift index 25c32671..c5185975 100644 --- a/Tuist/ProjectDescriptionHelpers/Project+Packages.swift +++ b/Tuist/ProjectDescriptionHelpers/Project+Packages.swift @@ -9,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") @@ -23,6 +27,7 @@ public enum DevLogPackages { ) public static let presentationPackageDependencies: [TargetDependency] = [ + .package(product: "ComposableArchitecture"), .package(product: "MarkdownUI"), .package(product: "OrderedCollections"), ] @@ -41,6 +46,7 @@ public enum DevLogPackages { public static let defaultPackages: [Package] = [] public static let presentationPackages: [Package] = [ + composableArchitecturePackage, markdownUIPackage, swiftCollectionsPackage, ] From 8bcd1478fd3961cbad17df92590415b70ad2d924 Mon Sep 17 00:00:00 2001 From: opficdev <162981733+opficdev@users.noreply.github.com> Date: Thu, 4 Jun 2026 23:36:12 +0900 Subject: [PATCH 3/4] =?UTF-8?q?chore:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20Swif?= =?UTF-8?q?tLint=20=EC=84=A4=EC=A0=95=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Application/DevLogApp/Project.swift | 12 +++++++++++- .../ProjectDescriptionHelpers/Project+Packages.swift | 9 ++++++--- .../Project+Templates.swift | 12 +++++++++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Application/DevLogApp/Project.swift b/Application/DevLogApp/Project.swift index 2d0053b6..992d31d7 100644 --- a/Application/DevLogApp/Project.swift +++ b/Application/DevLogApp/Project.swift @@ -25,7 +25,10 @@ let project = Project( ], entitlements: .file(path: "Sources/Resource/DevLog.entitlements"), scripts: [ - DevLogScripts.swiftLint(sourcePath: "Sources"), + DevLogScripts.swiftLint( + sourcePath: "Sources", + configPath: "Sources/.swiftlint.yml" + ), ], dependencies: [ .project(target: "DevLogPresentation", path: "../DevLogPresentation"), @@ -60,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"), ], @@ -67,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/Tuist/ProjectDescriptionHelpers/Project+Packages.swift b/Tuist/ProjectDescriptionHelpers/Project+Packages.swift index c5185975..ebd7e12d 100644 --- a/Tuist/ProjectDescriptionHelpers/Project+Packages.swift +++ b/Tuist/ProjectDescriptionHelpers/Project+Packages.swift @@ -59,7 +59,10 @@ public enum DevLogPackages { } public enum DevLogScripts { - public static func swiftLint(sourcePath: String) -> TargetScript { + public static func swiftLint( + sourcePath: String, + configPath: String = "../../.swiftlint.yml" + ) -> TargetScript { TargetScript.pre( script: """ export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH" @@ -70,7 +73,7 @@ public enum DevLogScripts { exit 1 fi - configPath="${SRCROOT}/../../.swiftlint.yml" + configPath="${SRCROOT}/\(configPath)" sourcePathName="\(sourcePath)" lintSourcePath="${SRCROOT}/${sourcePathName}" @@ -86,7 +89,7 @@ public enum DevLogScripts { """, name: "SwiftLint", inputPaths: [ - "$(SRCROOT)/../../.swiftlint.yml", + "$(SRCROOT)/\(configPath)", "$(SRCROOT)/\(sourcePath)", ], basedOnDependencyAnalysis: false, diff --git a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift index 4642acb5..f6a53760 100644 --- a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift +++ b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift @@ -20,7 +20,10 @@ public extension Project { infoPlist: .file(path: frameworkInfoPlistPath), sources: ["Sources/**/*.swift"], scripts: [ - DevLogScripts.swiftLint(sourcePath: "Sources"), + DevLogScripts.swiftLint( + sourcePath: "Sources", + configPath: "Sources/.swiftlint.yml" + ), ], dependencies: dependencies, settings: .devlog( @@ -41,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), ] ) From 505456ab09e9e52195090d58ffd3d7f382280f33 Mon Sep 17 00:00:00 2001 From: opficdev <162981733+opficdev@users.noreply.github.com> Date: Thu, 4 Jun 2026 23:54:49 +0900 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20SwiftLint=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=8B=A4=ED=96=89=20=EB=B0=A9=EC=8B=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Project+Packages.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Tuist/ProjectDescriptionHelpers/Project+Packages.swift b/Tuist/ProjectDescriptionHelpers/Project+Packages.swift index ebd7e12d..803fe537 100644 --- a/Tuist/ProjectDescriptionHelpers/Project+Packages.swift +++ b/Tuist/ProjectDescriptionHelpers/Project+Packages.swift @@ -80,11 +80,16 @@ public enum DevLogScripts { if [ "$sourcePathName" != "." ]; then "$swiftLintPath" lint --config "$configPath" "$lintSourcePath" else - status=0 - while IFS= read -r swiftFilePath; do - "$swiftLintPath" lint --config "$configPath" "$swiftFilePath" || status=$? - done < <(find "$lintSourcePath" -name "*.swift" -not -path "*/Derived/*" -not -name "Project.swift") - exit "$status" + 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",