はじめに
前回書いたRuby版のDanger導入方法に続き、今回はSwift製のDangerであるDanger Swiftでの導入を試しためましたので、その方法やハマりポイントについて記事にしようと思います。
環境
- Xcode16
- Swift6.0
- macOS 15.0
- CI: GitHub Actions
導入方法
基本的に公式の導入方法を参考に進めていきます。
https://danger.systems/swift/guides/getting_started
1. SPMでDanger Swiftの依存を持ったダイナミックライブラリを作成する
dependencies: [
.package(url: "https://github.com/danger/swift.git", exact: "3.21.1")
],
targets: [
.target(
name: "DangerDependencies",
dependencies: ["Danger"]
)
]
- dependencies に Danger を追加する
- Danger に依存したターゲットを追加
ただ、公式の方法でtargetにDangerへの依存を追加しようとしてもビルドエラーが出てしまい、以下のようにname
とpackage
を指定してやる必要がありました。
targets: [
.target(
name: "DangerDependencies",
- dependencies: ["Danger"]
+ dependencies: [
+ .product(name: "Danger", package: "swift")
+ ]
)
]
最終的な Package.swift の内容が以下になります。
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Dangerfile",
platforms: [.macOS(.v14)],
products: [
.library(
name: "Dangerfile",
type: .dynamic,
targets: ["Dangerfile"]
),
],
dependencies: [
.package(url: "https://github.com/danger/swift.git", exact: "3.21.1")
],
targets: [
.target(
name: "Dangerfile",
dependencies: [
.product(name: "Danger", package: "swift")
]
)
]
)
記事を参考に、ルートディレクトリにDangerfile
フォルダーを作り、
先ほど作成したSPMライブラリを全て突っ込みます。
ここまでで、Danger Swiftをコマンドラインで実行するためのダイナミックライブラリの作成準備までできました。
2. Dangerfileを作る
次にDangerfile
というものを作ります。
前回の記事でDangerfileがどういうものかは説明していますので、よければこちらもご覧ください。
ここでは何をどのように自動レビューするのかを定義するファイルという説明に留めます。
編集方法は公式ドキュメントを参照したところ、以下コマンドを打てば良さそうです。
swift build
swift run danger-swift edit
ここで、以下エラーにつまづきました。
Building for debugging...
[1/1] Write swift-version-7ECE0B649383FE20.txt
Build of product 'danger-swift' complete! (0.28s)
ERROR: Could not find a libDanger to link against at any of: [".build/debug", ".build/x86_64-unknown-linux/debug", ".build/release", "/usr/local/lib/danger", "/opt/homebrew/lib/danger", "/Users/satoutaichi/.local/share/mise/installs/danger-swift/3.21.1/lib/danger"]
Or via Homebrew, or Marathon, or Mise
libDanger
がないと言っており、.build/debug
にはlibDangerfile
というファイルがあったので、Package.swiftのlibrary名に関係していると思い、library名をDanger
に修正したところ、libDanger
が作成されるようになりました。
products: [
.library(
- name: "Dangerfile",
+ name: "Danger",
type: .dynamic,
targets: ["MachoDanger"]
),
],
上記に修正したら、再度以下コマンドを実行します。
swift build
swift run danger-swift edit
上記コマンドを打つと、xcode が開いて Dangerfile を編集できるようになりました。
以下のような画面が開きます。
ここで、Sources/Dangerfile/main.swift
を編集して以下のようにしてみます。
import Danger
let danger = Danger()
if let github = danger.github {
// PRのタイトルが "WIP" を含んでいる場合、警告を出す
if github.pullRequest.title.contains("WIP") {
warn("PRが 'WIP' のままです。完了したらタイトルを変更してください。")
}
// PRの説明が短すぎる場合に警告を出す
if let body = github.pullRequest.body, body.count < 100 {
warn("PRの説明が短すぎます。詳細を追加してください。")
}
// 変更行が500行を超える場合に警告を出す
let changeFilesCount = github.pullRequest.additions ?? 0
if changeFilesCount > 500 {
warn("変更行数が500行を超えています(修正行: \(changeFilesCount))。PRを小さく分けられるか検討してください。")
}
}
上記XcodeでDangerfile修正後、swift run danger-swift edit
を実行したコンソールで、Enterを押下するとDangerfileの編集が完了します。(逆にいうとEnterを押して正しくeditモードを終了しないと保存されないので注意が必要です!)
ℹ️ Danger will keep running, in order to commit any changes you make in Xcode back to the original script file
Press the return key once you're done
3. DangerJSをインストールする
一旦ローカル確認ようにDangerJSをインストールします。
インストールは以下コマンドを実行すれば良いです。
npm install -g danger
上記インストールするにはnpm
が必要になるので、もし入っていなければ以下あたりを参考にすれば良いかなと思います。
4. ローカルでDangerの動作確認をする
公式のREADMEに倣って、以下コマンドを実行してみます。
swift run danger-swift local --base main
% swift run danger-swift local --base BR_develop-1.0 --verbose
Building for debugging...
[1/1] Write swift-version-7ECE0B649383FE20.txt
Build of product 'danger-swift' complete! (0.21s)
Launching Danger Swift local (v3.20.2)
Finding out where the danger executable is
Running: /Users/satoutaichi/.nvm/versions/node/v18.16.0/bin/danger local --process .build/debug/danger-swift --passURLForDSL --base BR_develop-1.0 --verbose
/var/folders/dz/_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger/12C0154A-9691-458D-94AB-EE1F0A8D8FF0/_tmp_dangerfile.swift:3:14: error: cannot call value of non-function type 'module<Danger>'
1 | import Danger
2 | import Foundation
3 | let danger = Danger()
| `- error: cannot call value of non-function type 'module<Danger>'
4 |
Launching Danger Swift runner (v3.20.2)
Got URL for JSON: /var/folders/dz/_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger-dsl-c1899073.json
Created a temporary file for the Dangerfile DSL at: /var/folders/dz/_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger-dsl-c1899073.json
Running Dangerfile at: Dangerfile.swift
Preparing to compile
Running: /Applications/Xcode16.2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift --driver-mode=swift -L /Users/satoutaichi/work/product/AkiraDiary/Danger/Dangerfile/.build/debug -I /Users/satoutaichi/work/product/AkiraDiary/Danger/Dangerfile/.build/debug -lDanger /var/folders/dz/_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger/12C0154A-9691-458D-94AB-EE1F0A8D8FF0/_tmp_dangerfile.swift runner /Users/satoutaichi/.nvm/versions/node/v18.16.0/lib/node_modules/danger/distribution/commands/danger-local.js --process .build/debug/danger-swift --passURLForDSL --base BR_develop-1.0 --verbose /var/folders/dz/_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger-dsl-c1899073.json /var/folders/dz/_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger/12C0154A-9691-458D-94AB-EE1F0A8D8FF0/danger-response.json
Completed evaluation
ERROR: Dangerfile eval failed at Dangerfile.swift
ERROR: Could not get the results JSON file at /var/folders/dz/_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger/12C0154A-9691-458D-94AB-EE1F0A8D8FF0/danger-response.json
Danger: ⅹ Failing the build, there is 1 fail.
## Failures
`danger-swift` failed.
## Markdowns
### Log
<details>
/var/folders/dz/\_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger/12C0154A-9691-458D-94AB-EE1F0A8D8FF0/\_tmp_dangerfile.swift:3:14: error: cannot call value of non-function type 'module<Danger>'
1 | import Danger
2 | import Foundation
3 | let danger = Danger()
| `- error: cannot call value of non-function type 'module<Danger>'
4 |
Launching Danger Swift runner (v3.20.2)
Got URL for JSON: /var/folders/dz/\_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger-dsl-c1899073.json
Created a temporary file for the Dangerfile DSL at: /var/folders/dz/\_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger-dsl-c1899073.json
Running Dangerfile at: Dangerfile.swift
Preparing to compile
Running: /Applications/Xcode16.2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift --driver-mode=swift -L /Users/satoutaichi/work/product/AkiraDiary/Danger/Dangerfile/.build/debug -I /Users/satoutaichi/work/product/AkiraDiary/Danger/Dangerfile/.build/debug -lDanger /var/folders/dz/\_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger/12C0154A-9691-458D-94AB-EE1F0A8D8FF0/\_tmp_dangerfile.swift runner /Users/satoutaichi/.nvm/versions/node/v18.16.0/lib/node_modules/danger/distribution/commands/danger-local.js --process .build/debug/danger-swift --passURLForDSL --base BR_develop-1.0 --verbose /var/folders/dz/\_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger-dsl-c1899073.json /var/folders/dz/\_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger/12C0154A-9691-458D-94AB-EE1F0A8D8FF0/danger-response.json
Completed evaluation
ERROR: Dangerfile eval failed at Dangerfile.swift
ERROR: Could not get the results JSON file at /var/folders/dz/\_vzvz7yn6hg_by51jhrw1tz80000gn/T/danger/12C0154A-9691-458D-94AB-EE1F0A8D8FF0/danger-response.json
</details>
この問題は、Package.swiftのlibrary名に原因があったようで、
名前をDangerDeps
にすると 動作するようになりました。
products: [
.library(
- name: "Danger",
+ name: "DangerDeps",
type: .dynamic,
targets: ["Dangerfile"]
),
],
Danger Swift のコードを確認してみたところ、libraly 名の頭にDangerDeps
が付いていないとSPMDanger
を初期化せず実行内容が少し変わっているため、そこに原因がありそうでした。
(この問題はXcode16系の環境でしか起きず、Xcode15系の場合では発生しなかったです!罠すぎる、、)
ただ、公式の README でも library 名はDangerDeps
が付いていたため公式の方法に準拠するのが良さそうですね。
改めて、ローカル確認をしてみると、うまくいきました。
% swift run danger-swift local --base main
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/.build/checkouts/octokit.swift/OctoKit/Info.plist
Building for debugging...
[1/1] Write swift-version-7ECE0B649383FE20.txt
Build of product 'danger-swift' complete! (0.61s)
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/.build/checkouts/octokit.swift/OctoKit/Info.plist
Danger: ✓ passed review, received no feedback.
以下を実行すると、実際のPRに対してローカルでDangerの自動レビュー結果を見ることができますのでこれも試してみます。
swift run danger-swift pr <PRのURL>
ただこのコマンドでは実際のPRを参照する都合上、GitHub APIを使用することになるので、GitHub API実行用のGitHubトークンが必要になります。
GitHubトークンをDANGER_GITHUB_API_TOKEN
という名前の環境変数に設定することで動こかせるようになるはずです。
export DANGER_GITHUB_API_TOKEN=<GitHubトークン>
% swift run danger-swift pr https://github.com/stotic-dev/DangerSwiftModule/pull/2
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/.build/checkouts/octokit.swift/OctoKit/Info.plist
Building for debugging...
[1/1] Write swift-version-7ECE0B649383FE20.txt
Build of product 'danger-swift' complete! (0.66s)
Starting Danger PR on stotic-dev/DangerSwiftModule#2
You don't have a DANGER_GITHUB_API_TOKEN set up, this is optional, but TBH, you want to do this
Check out: http://danger.systems/js/guides/the_dangerfile.html#working-on-your-dangerfile
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/.build/checkouts/octokit.swift/OctoKit/Info.plist
Danger: ✓ found only warnings, not failing the build
## Warnings
PRの説明が短すぎます。詳細を追加してください。
-
変更行数が500行を超えています(修正行: 16239)。PRを小さく分けられるか検討してください。
一部 Warning が出ているが、ちゃんと動いてくれていそうです!
5. CIでDanger Swiftを動かせるようにする
今回は GitHub Actions で動かす方法で試します。
以下は GitHub Actions のワークフローファイルです。
name: danger-ci_xcode_16
on:
pull_request:
branches: "*"
jobs:
build:
# 実行環境はmacosに設定
runs-on: macos-latest
steps:
# fetch-depth の計算
- run: echo "fetch_depth=$(( commits + 1 ))" >> $GITHUB_ENV
env:
commits: ${{ github.event.pull_request.commits }}
# チェックアウト(リポジトリからソースコードを取得)
- uses: actions/checkout@v4.2.2
with:
fetch-depth: ${{ env.fetch_depth }}
# Xcodeのバージョン一覧を出力
- name: Show Xcode list
run: ls /Applications | grep 'Xcode'
# Xcodeのバージョンを指定
- name: Set Xcode Path
run: sudo xcode-select --switch /Applications/Xcode_16.2.app/Contents/Developer
# 現在のXcodeバージョンを確認
- name: Show Xcode version
run: xcodebuild -version
# danger jsのインストール
- name: Install Danger JS
run: npm install -g danger
# dangerを実行
- name: Analytics with danger
run: |
swift run --disable-sandbox danger-swift ci
env:
# Dangerはプルリクエストを読み取ってコメントをするので、必要な権限を持ったGithubトークンを渡してあげる必要がある
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_BOT_GITHUB_TOKEN }}
補足
リポジトリのチェックアウト
PRのベースブランチと比較して何かしらのレビューをしたいケースがあると思うので、こちらの記事で、ベースブランチ - 現在のPRの情報までをチェックアウトするようにします。
.build
をリポジトリにPushする
SPMでDangerを導入すると、どうしてもSPMのビルド時間がかかってしまうので、swift build
の成果物である.build
自体もリポジトリにPushすることで、GitHub ActionsのステップではSPMのビルドは省略し、いきなりswift run
によるDangerの実行を行うようにしています。
これは、公式でも推奨する方法です。
Danger で SwiftLint の警告やエラーが起きていないかも自動レビューしてもらう
Danger Swift は Ruby 版と違い、SwiftLint の Plugin はデフォルトで入っています。
ただ、Danger で SwiftLint を使用するときに内部的に swiftlint コマンドを使用する都合上、swiftlint のバイナリを容易する必要があります。
今回はこちらの記事を参考に、SPM に SwiftLint の依存を追加して SwiftLint のバイナリを使えるようにします。
具体的には Package.swift を以下のようにします。
import PackageDescription
let package = Package(
name: "Dangerfile",
platforms: [.macOS(.v14)],
products: [
.library(name: "DangerDeps",
type: .dynamic,
targets: ["Dangerfile"]),
],
dependencies: [
.package(url: "https://github.com/danger/swift.git", exact: "3.21.1"),
+ .package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", exact: "0.58.2"),
],
targets: [
.target(
name: "Dangerfile",
dependencies: [
.product(name: "Danger", package: "swift")
]
)
]
)
SwiftLint には以下のリポジトリのものもあるが、あくまで swiftlint のバイナリを使いたいだけで以下は不要な依存が含まれておりサイズが大きすぎるので、SwiftLintPlugins
の方を使用しています。
https://github.com/realm/SwiftLint
Dangerfile には以下を追記します。
+let lintPath = SwiftLint.SwiftlintPath.bin(".build/artifacts/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/swiftlint-0.58.2-macos/bin/swiftlint")
+
+SwiftLint.lint(.modifiedAndCreatedFiles(directory: "<Lint対象のディレクトリ>"),
+ inline: true,
+ configFile: "<リポジトリのルートディレクトリから見た.swiftlint.ymlのファイルパス>",
+ swiftlintPath: lintPath)
lint
関数の一つ目の引数には、SwiftLint.LintStyle
を設定する。
定義は以下のような感じになっています。
public enum LintStyle {
/// Lints all the files instead of only the modified and created files.
/// - Parameters:
/// - directory: Optional property to set the --path to execute at.
case all(directory: String?)
/// Only lints the modified and created files with `.swift` extension.
/// - Parameters:
/// - directory: Optional property to set the --path to execute at.
case modifiedAndCreatedFiles(directory: String?)
/// Lints only the given files. This can be useful to do some manual filtering.
/// The files will be filtered on `.swift` only.
case files([File])
}
今回設定した以下は、PR の変更ファイルの内、directory
に指定したディレクトリに含まれるファイルを対象に Lint することを表します。
.modifiedAndCreatedFiles(directory: "<Lint対象のディレクトリ>")
inline
の引数では、Lint で検知した警告やエラーをインラインにコメント(つまり問題のあるコードの行に対してレビューコメントをする)するかどうかを表します。
SwiftLint の静的解析によるレビューに関しては、具体的にどの部分が問題なのかを知りたいので、大抵の場合はインラインでのコメントにした方が、幸せになるケースが多いと思います。
configFile
の引数には、どのようなルールで静的解析するのかを定義する.swiftlint.yml
のパスを指定します。
もしこの引数を設定しない場合は、SwiftLint のデフォルトのルールで静的解析することになるので必須の引数ではありません。
(swiftlint コマンドの--config
オプションを指定しないのと同じ)
swiftlintPath
の引数では、swiftlint を実行するためのバイナリを指定するための情報を設定します。
この引数の型はSwiftLint.SwiftlintPath
で内容は以下のようになっています。
public enum SwiftlintPath {
case swiftPackage(String)
case bin(String)
var command: String {
switch self {
case let .bin(path):
return path
case let .swiftPackage(path):
return "swift run --package-path \(path) swiftlint"
}
}
}
swiftPackage(String)
はswift run
コマンドで swiftlint を実行できる Package.swift のディレクトリを指定することで、SwiftlintPath を正しく設定することができます。
bin(String)
は swiftlint のバイナリの相対パス(Dangerfile から見た)を指定することで SwiftlintPath を正しく設定することができます。
今回だと用意した SwiftLint の依存がSwiftLintPlugins
でこのライブラリの Package.swift にはexecutable
な product がなくswift run
から実行することができないので、
bin(String)
で swiftlint のバイナリを直接指定します。
let lintPath = SwiftLint.SwiftlintPath.bin(".build/artifacts/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/swiftlint-0.58.2-macos/bin/swiftlint")
Package.swift と Dangerfile の修正は一旦完了したので、ローカルで動作するか確認してみます。
swift run danger-swift local --cwd .. --base main
ただこれだと以下エラーが発生します。
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/Dangerfile/.build/checkouts/octokit.swift/OctoKit/Info.plist
Building for debugging...
[49/49] Applying danger-swift
Build of product 'danger-swift' complete! (3.37s)
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/Dangerfile/.build/checkouts/octokit.swift/OctoKit/Info.plist
The operation couldn’t be completed. (SwiftLintCore.Issue error 15.)
Could not read configuration: file Configuration.swift, line 289
/bin/sh: line 1: 68032 Abort trap: 6 .build/artifacts/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/swiftlint-0.58.2-macos/bin/swiftlint lint --reporter json --quiet --config "DangerSample/.swiftlint.yml" --use-script-input-files --force-exclude > /var/folders/dz/_vzvz7yn6hg_by51jhrw1tz80000gn/T/swiftlintReport.json
Danger: ✓ passed review, received no feedback.
これは Dangerfile で指定した.swiftlint.yml
のパスを指定が誤っており、見つからなかったためでした。
.swiftlint.yml
のパス指定を以下のように修正したら、この問題は解消されました。
let lintPath = SwiftLint.SwiftlintPath.bin(".build/artifacts/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/swiftlint-0.58.2-macos/bin/swiftlint")
SwiftLint.lint(.modifiedAndCreatedFiles(directory: "<Lint対象のディレクトリ>"),
inline: true,
- configFile: "<リポジトリのルートディレクトリから見た.swiftlint.ymlのファイルパス>",
+ configFile: "<Dangerfileの場所から相対的.swiftlint.ymlのファイルパス>",
swiftlintPath: lintPath)
これで再度danger-swift local
を実行します。
先ほどの事象は解消されたが、Lint 対象のファイルが読み込めず静的解析ができない問題にぶち当たりました。
% swift run danger-swift local --cwd ../ --base main
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/Dangerfile/.build/checkouts/octokit.swift/OctoKit/Info.plist
Building for debugging...
[1/1] Write swift-version-7ECE0B649383FE20.txt
Build of product 'danger-swift' complete! (0.52s)
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/Dangerfile/.build/checkouts/octokit.swift/OctoKit/Info.plist
Could not read contents of `/Users/satoutaichi/work/swift/DangerSwiftModule/Dangerfile/DangerSample/DangerSample/ContentView.swift`
Danger: ✓ passed review, received no feedback.
以下ログから読み取れるのは、存在しないパスを参照しに行っているということ。
Could not read contents of `/Users/satoutaichi/work/swift/DangerSwiftModule/Dangerfile/DangerSample/DangerSample/ContentView.swift`
danger-swift の--cwd
でワーキングディレクトリを指定できるらしいのですが、これがなぜか効かないため、swiftlint もリポジトリのルートディレクトリ/Dangerfile
から実行されており Lint 対象のファイルをうまく指定できていないっぽいです。(この問題わかる人いたら教えて欲しいです、、)
これの根本的な解決法は現状思いついてないですが、Dangerfile にある SPM のファイルたちをリポジトリのルートディレクトリに配置するとうまく動くようになりました。
Dangerfile をリポジトリのルートディレクトリに移動したので、.swiftlint.yml
の指定パスも以下のように変更します。
let lintPath = SwiftLint.SwiftlintPath.bin(".build/artifacts/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/swiftlint-0.58.2-macos/bin/swiftlint")
SwiftLint.lint(.modifiedAndCreatedFiles(directory: "<Lint対象のディレクトリ>"),
inline: true,
- configFile: "<Dangerfileの場所から相対的.swiftlint.ymlのファイルパス>",
+ configFile: "<リポジトリのルートディレクトリから見た.swiftlint.ymlのファイルパス>",
swiftlintPath: lintPath)
これで再度 local での danger-swift の動作確認を行いました。
% swift run danger-swift local --base main
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/.build/checkouts/octokit.swift/OctoKit/Info.plist
Building for debugging...
[49/49] Applying danger-swift
Build of product 'danger-swift' complete! (4.18s)
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/.build/checkouts/octokit.swift/OctoKit/Info.plist
Danger: ✓ found only warnings, not failing the build
## Warnings
Line should be 110 characters or less; currently it has 130 characters (`line_length`)
-
Lines should not have trailing whitespace (`trailing_whitespace`)
-
Lines should not have trailing whitespace (`trailing_whitespace`)
-
Lines should not have trailing whitespace (`trailing_whitespace`)
-
Lines should not have trailing whitespace (`trailing_whitespace`)
-
Limit vertical whitespace to a single empty line; currently 4 (`vertical_whitespace`)
SwiftLint がきちんと動作してくれた!
これでリポジトリを Push すると CI でも SwiftLint の静的解析を実行して、警告やエラーがあればレビューしてくれるようになります。
はまりポイント
SwiftLint を Danger Swift の Dangerfile で実行するようにして、danger-swift を実行すると以下のようなエラーが発生するようになる場合があります。
% swift run danger-swift local --base main
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/.build/checkouts/octokit.swift/OctoKit/Info.plist
Building for debugging...
[1/1] Write swift-version-7ECE0B649383FE20.txt
Build of product 'danger-swift' complete! (0.62s)
warning: 'octokit.swift': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Users/satoutaichi/work/swift/DangerSwiftModule/.build/checkouts/octokit.swift/OctoKit/Info.plist
## Failures
Error deserializing SwiftLint JSON response ([
{
"character" : null,
"file" : "/Users/satoutaichi/work/swift/DangerSwiftModule/DangerSample/DangerSample/ContentView.swift",
"line" : 13,
"reason" : "Line should be 110 characters or less; currently it has 130 characters",
"rule_id" : "line_length",
"severity" : "Warning",
"type" : "Line Length"
},
{
"character" : null,
"file" : "/Users/satoutaichi/work/swift/DangerSwiftModule/DangerSample/DangerSample/ContentView.swift",
"line" : 14,
"reason" : "Lines should not have trailing whitespace",
"rule_id" : "trailing_whitespace",
"severity" : "Warning",
"type" : "Trailing Whitespace"
},
{
"character" : null,
"file" : "/Users/satoutaichi/work/swift/DangerSwiftModule/DangerSample/DangerSample/ContentView.swift",
"line" : 15,
"reason" : "Lines should not have trailing whitespace",
"rule_id" : "trailing_whitespace",
"severity" : "Warning",
"type" : "Trailing Whitespace"
},
{
"character" : null,
"file" : "/Users/satoutaichi/work/swift/DangerSwiftModule/DangerSample/DangerSample/ContentView.swift",
"line" : 16,
"reason" : "Lines should not have trailing whitespace",
"rule_id" : "trailing_whitespace",
"severity" : "Warning",
"type" : "Trailing Whitespace"
},
{
"character" : null,
"file" : "/Users/satoutaichi/work/swift/DangerSwiftModule/DangerSample/DangerSample/ContentView.swift",
"line" : 17,
"reason" : "Lines should not have trailing whitespace",
"rule_id" : "trailing_whitespace",
"severity" : "Warning",
"type" : "Trailing Whitespace"
},
{
"character" : null,
"file" : "/Users/satoutaichi/work/swift/DangerSwiftModule/DangerSample/DangerSample/ContentView.swift",
"line" : 17,
"reason" : "Limit vertical whitespace to a single empty line; currently 4",
"rule_id" : "vertical_whitespace",
"severity" : "Warning",
"type" : "Vertical Whitespace"
}
]
Your version of SwiftLint is up to date.
): dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Unexpected character 'Y' after top-level value around line 57, column 1." UserInfo={NSJSONSerializationErrorIndex=1956, NSDebugDescription=Unexpected character 'Y' after top-level value around line 57, column 1.})))
Danger: ⅹ Failing the build, there is 1 fail.
このエラーは SwiftLint の実行結果の json を Danger Swift がパースするときに予期せぬ文字が入り込んでしまっているためにパースに失敗することで発生するエラーです。
具体的には、以下 json のYour version...
の文字が入っており純粋な json 形式以外の文字が SwiftLint によって出力されているのが問題。
[
・・・省略
]
Your version of SwiftLint is up to date. # ⇦この文字が悪い
これは指定している.swiftlint.yml
の以下設定をfalse
もしくは削除することで解消できます。
# If true, SwiftLint will check for updates after linting or analyzing.
check_for_updates: true
おわり
以上が、SPMでのDangerの導入方法でした。
ちょくちょくハマりポイントがあり、初見の導入には苦労した印象ですが、
わかってしまえば簡単かもしれないですね。
ただ、結局未解決の問題(--cwdオプションが効かない問題)があったり、DangerJSに依存していたりと面倒な部分があるのも事実でした。
とはいえ、DangerfileをSwiftでかけるのは、個人的にはメンテしやすくてかなり良いと感じたので事情がない限りはSPMでの導入が良いかなと思いました。
SPMでのDangerの導入を試すためのサンプルプロジェクトを作っているので、よければこちらも参考にしていただければと思います。