前書き
以前 Danger-Swift 移行の手引き記事を書きましたが、その記事には残念ながら一つ大きな問題点があります:プラグインの利用は Marathon というすでに Deprecated 済みのツールに依存しています。もちろん Danger-Swift 公式はちゃんとした SwiftPM を利用した方法も紹介していますが、微妙に面倒だったので自分はこれまで避けてきました。
でもやはり Deprecated なものにいつまでも依存してはリスクが高いです;また場合によってプライベートなプラグインを利用したり、最新リリースではなく特定のバージョンやブランチを利用したいこともありますが、そんな時は SwiftPM の利用が必須です。というわけで、この記事でちゃんと SwiftPM を利用したプラグインの導入の仕方を紹介したいと思います。
ただし、残念ながら現在 M1 Mac の場合、ローカルで実行できないことがマシンによってあるようです。CI 環境が Intel Mac であれば問題ないですが、M1 なら注意が必要です。
ローカル環境の整備
Danger 用のフォルダーを作成
まず手始めに、Danger 用のフォルダーを作っておきます。もちろんこれを作らなくても、動作させる分には問題ないですが、その場合ルートディレクトリーに Package.swift
ファイルを置くことになります。このやり方は少なくとも 2 つのデメリットがあります:
-
xed .
コマンドの動作が変わってしまいます。
ターミナルから Xcode を起動させるのにxed
というコマンドがあります。このコマンドに今のディレクトリー.
を渡してあげれば、xed
は自動的に適切なファイルを開いてくれますが、残念ながら優先順位としては*.xcworkspace
よりも、Package.swift
ファイルの方が優先されます、つまりルートにPackage.swift
を置いておくと、xed
はこのディレクトリーを Swift Package のプロジェクトとして認識してしまいます。 -
人間の目にも優しくないです。
xed
を使わなくても、結果としてルートディレクトリーにPackage.swift
を置いていると、人間がぱっと見これが正規なプロジェクトに関係する設定と認識してしまいますので、フォルダー構成としても非常に紛らわしいです。おまけに Danger-Swift に関係するファイルはこれだけでなく、他にもDangerfile.swift
やSources
などがありますので、ルートディレクトリーが非常に煩雑になってしまいやすいです。
というわけで、Danger 用のファイルは全部 Danger
フォルダーにぶち込んでおきましょう。
Packages.swift を作成
Marathon を利用しないので、代わりに依存を Packages.swift で指定する必要があります。ここで必要なのは Danger-Swift 自身と、利用するプラグインのパッケージです。パッケージ構成としては、プロダクトとして DangerDepsProduct
というライブラリーを作る必要があります、そしてこのプロダクトが含まれるターゲットは DangerDependencies
で、このターゲットには Danger-Swift と各プラグインが含まれています。公式でも大まかなサンプルが用意されていますが、ここでより詳細なサンプルも上げておきます:
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Dangerfile",
platforms: [.macOS("10.12")],
products: [
.library(name: "DangerDepsProduct",
type: .dynamic,
targets: ["DangerDependencies"]),
],
dependencies: [
// Danger
.package(name: "danger-swift", url: "https://github.com/danger/swift.git", from: "3.0.0"),
// Danger Plugins
.package(name: "DangerXCodeSummary", url: "https://github.com/f-meloni/danger-swift-xcodesummary.git", from: "1.2.0"),
.package(name: "DangerSwiftCoverage", url: "https://github.com/f-meloni/danger-swift-coverage.git", from: "1.2.0"),
.package(name: "DangerSwiftEda", url: "https://github.com/yumemi-inc/danger-swift-eda.git", from: "0.1.0"),
],
targets: [
.target(
name: "DangerDependencies",
dependencies: [
.product(name: "Danger", package: "danger-swift"),
"DangerXCodeSummary",
"DangerSwiftCoverage",
"DangerSwiftEda",
]
),
]
)
ここで注意が必要なのは Danger-Swift 本体の取り入れ方でしょうか、これはパッケージアドレスとパッケージ名とプロダクト名がそれぞれ全部違うため、dependencies
の宣言では文字列そのままが使えず、必ず .product(name:package:)
で導入する必要があります1。他のプラグインも、もしパッケージ名とプロダクト名が違ったら、このように導入する必要があります。
そのほかに、プロダクトの宣言で type
は必ず .dynamic
にしておく必要があります。また、platforms
指定も、空欄にしておくとデフォルトでは .macOS("10.10")
が使われますが、利用するライブラリーによってそれより高いバージョンが必要になることがあるので、その場合はそれに合わせて platforms
の指定も必要です。
Dangerfile.swift を修正
Marathon を使わなくなったので、Dangerfile.swift
に書かれてある各種アドレスも必要なくなるから、それらを削除しておきましょう。
ローカルでの動作確認
Danger-Swift を動かすのに、Danger-JS も必要です。以前の手引き記事ですでに brew
などでインストール済みであれば問題ないですが、もしまだであれば npm
で Danger-JS をインストールしましょう。また npm
自体のインストールは brew install node
でできます。npm
がインストールされたら、npm install danger
で Danger-JS をインストールできます。
Danger-JS がインストールされたら、今度はターミナルで Danger のディレクトリーに移動して、swift run danger-swift edit
をすれば、Danger-Swift の実行に必要なものを Swift が全部落としてきてビルドして、Dangerfile.swift
の編集画面を開いてくれます。ただし今ターミナルのディレクトリーはプロジェクトではなく Danger
というフォルダにあるので、pr
や ci
コマンドには --cwd ../
2 3 パラメーターを付与する必要があります。なので例えばローカルで PR チェックを実行したい場合は、swift run danger-swift pr <url/to/pull-request> --cwd ../
を実行すれば OK です。
CI での導入
こちらもローカルの動作確認と同じように npm
で Danger-JS をインストールしておく必要があります、そして Danger-Swift を実行するときもローカルの時と同じように、先に Danger
に移動してから、swift run danger ci --cwd ../
で実行する必要があります。
また、せっかく SwiftPM で導入したので、Marathon の時と違ってキャッシュが取りやすいから、それも CI にキャッシュさせておきましょう。キャッシュしておきたいパスは Danger/.build
です、ここには Danger-Swift の実行に必要なものが大体揃ってあります。これの生成は一度 swift run danger-swift <command>
を実行させるとできますが、もしキャッシュするタイミングが Danger の実行前であれば、先に swift build
しておくと大抵のものが先に生成されますので、実行時の処理がだいぶ軽くなります。ただし注意しないといけないのは Danger/.build
ディレクトリーはパッケージが実行やビルドされるたびに内容に差分ができてしまいますので、キャッシュのフラグとして Danger/Package.resolved
ファイルを指定しましょう、そうすればこのファイルが変更された時だけ Danger/.build
フォルダーがキャッシュされます。
SwiftLint についての TIPS
ここまでしたら、Danger-Swift の導入がプラグイン含めて全て SwiftPM でできるようになりましたが、実際のシナリオとして SwiftLint の利用も Danger-Swift 内でよくあります。ところでせっかくなので SwiftLint も SwiftPM で導入したい気持ちありますよね。というわけでここで Danger-Swift 向けの SwiftLint の導入についても少し TIPS を書かせてください。
まず、Danger-Swift 用の SwiftLint なので、依存も Danger-Swift と同じ Package.swift
内で宣言しておきましょう。そして Package.swift
を開いた時にワーニングが出ないように、ダミーのターゲットも作っておきましょう:
let package = Package(
// ...
dependencies: [
// ...
// SwiftLint
.package(name: "SwiftLint", url: "https://github.com/realm/SwiftLint", from: "0.45.0"), // Only for Danger-Swift runtime
],
targets: [
// ...
.target(
name: "SwiftLintDependencies", // Only for Danger-Swift runtime
dependencies: [
.product(name: "swiftlint", package: "SwiftLint"),
]
),
]
)
そうすれば SwiftLint の導入も SwiftPM でできます;また利用する際は、Dangerfile.swift
の SwiftLint
コマンドで swiftlintPath
を指定しましょう:
// ...
SwiftLint.lint(/* ... */, swiftlintPath: .swiftPackage("$(pwd)/Danger"))
ちなみにここの $(pwd)
は本来なら現在いるディレクトリー、つまり Danger
になるはずですが、おそらく --cwd ../
の影響で、Danger-Swift の実行が親ディレクトリーに移動されたため、$(pwd)
が実際取れてるのはプロジェクトのパスになります。そのため、Danger のディレクトリーにある SwiftLint を指定させるには、$(pwd)/Danger
にする必要があります。
後書き
ところで気づいた人がいるのでしょうか。そう、この記事に登場した Package.swift
のサンプルに、実は弊社が公開したプラグインもあります:DangerSwiftEda です。このプラグインは一体どんなものか、そしてそもそも Danger-Swift のプラグインはどう作ればいいのかーーDanger-Swift のプラグイン作成編を乞うご期待!
-
正確にはこのファイルの一番先頭に書かれてある
// swift-tools-version:
のバージョンによって違いますが、少なくとも 5.3〜5.5 のツールバージョンはこのような書き方が必要です。Danger-Swift の実行は作業プロジェクト自体のバージョンには全く依存しないのである程度の自由は効きますが、とは言え古すぎるバージョンはやはり避けるべきでしょう。 ↩ -
cwd
はcurrent working directory
、そして../
は親パスを指します。この仕様は公式ドキュメントからも確認できます。 ↩ -
Swift パッケージの実行には他にも
Danger
フォルダーに移動せず、プロジェクトにいたまま例えばswift run --package-path Danger danger-swift <command>
を実行する方法も本来ならありますが、残念ながらdanger-swift
の実行にはそれではlibDanger
のライブラリーのパス解決ができず、失敗してしまいますので、大人しくcd Danger
して--cwd ../
パラメーターを使いましょう。 ↩