Swift製コマンドラインツールのパッケージ管理ツール「Mint」のセットアップ&操作方法

## はじめに

もともと気になっており、Mockoloのインストールを機に導入することにしました。

## 「Mint」とは？

Swift製コマンドラインツールのインストールと実行を管理するツールです。

BundlerのSwift版と考えるとわかりやすいです。

## メリット

基本的に、Ruby製ライブラリをBundlerで管理するメリットと同様です。

- プロジェクト内でSwift製コマンドラインツールのバージョンを簡単に揃えられる
- プロジェクトごとに異なるバージョンのSwift製コマンドラインツールを使える

## 環境

- OS：macOS Catalina 10.15
- Mint：0.13.0

## セットアップ

### Mintのインストール

Homebrewからインストールします。

```shell-session
$ brew install mint
```

MakeやSwiftPMでインストールするには、公式ドキュメントをご参照ください。
https://github.com/yonaskolb/Mint#installing

### Mintのバージョン確認

Mintのバージョンを確認します。

```shell-session
$ mint version
Version: 0.13.0
```

## 操作方法

### Mintfileの作成

`Mintfile` という名前のファイルを作成し、使いたいパッケージを記述します。
Mintfileを作成しなくてもパッケージを管理できますが、プロジェクト内でパッケージのバージョンを揃える場合、Mintfileがあれば簡単です。

```:Mintfile
realm/SwiftLint@0.38.0
uber/mockolo@1.1.1
```

`{ユーザー名}/{リポジトリ名}@{バージョン}` の形式で記述します。

### パッケージのインストール

`mint bootstrap` コマンドを実行することで、 `Mintfile` に記述した全パッケージを __ローカル__ にインストールします。

```shell-session
$ mint bootstrap
```

`--verbose` オプションを付けるとログを詳細に出力します。
パッケージのインストールに失敗する場合に付けるといいです。

インストール済のパッケージはスキップされるので、 `Mintfile` の一部を編集しても効率よくインストールできます。

追記
0.16.0で `--overwrite` オプションが追加されました。
https://github.com/yonaskolb/Mint/releases/tag/0.16.0

`--overwrite y` を指定すると、バイナリがすでに存在する場合に `y` を自動で入力します。
CI時に付けるべきオプションです。

### インストール済パッケージの一覧

`mint list` コマンドを実行することで、インストール済パッケージの一覧を出力できます。

```
$ mint list
🌱  Installed mint packages:
  mockolo
    - 1.1.1
  SwiftLint
    - 0.38.0
```

### パッケージの実行

`mint run {パッケージ名} {コマンド}` で指定したパッケージのコマンドを実行できます。

```shell-session
$ mint run {パッケージ名} {コマンド}
```

例

```shell-session
$ mint run swiftlint swiftlint version
🌱  Using realm/SwiftLint 0.38.0 from Mintfile.
🌱  SwiftLint 0.38.0 already installed
🌱  Running swiftlint 0.38.0...
0.38.0
```

Mintの特徴として、パッケージの実行時にインストールされていない場合は自動でインストールします。
CI/CD環境では `mint bootstrap` せず、必要なときに必要なパッケージのみインストールすると、1つのMintfileで複数のワークフローを効率よく使えます。

### パッケージのアンインストール

`mint uninstall {パッケージ名}@{バージョン}` で、指定したパッケージをアンインストールします。

```shell-session
$ mint uninstall {パッケージ名}@{バージョン}
```

パッケージは複数のバージョンをインストールできるため、基本的にはバージョンを指定してアンインストールしてください。
バージョンを指定しない場合、 __全バージョンをアンインストールします__ 。

### パッケージのバージョンアップ

`Mintfile` 内のバージョンを変更して `mint bootstrap` するのみです。
古いバージョンも残るため、不要なら `mint uninstall {パッケージ名}@{バージョン}` してください。

### おまけ：ローカルのインストールパス

パッケージはプロジェクトフォルダ内でなく、デフォルトで `/usr/local/lib/mint` にインストールされます。
そのため、CocoaPodsやCarthageのように `.gitignore` へインストールフォルダを追記しなくてもバージョン管理外になります。

インストールフォルダ内をざっと確認したところ、パッケージをプロジェクト名で管理していないようでした。
異なるプロジェクト間で同じバージョンのパッケージを使う場合、同じフォルダを参照すると思われます。

CIでは使いづらいので、 `MINT_PATH` に `mint/lib` 、 `MINT_LINK_PATH` に `mint/bin` などを指定するといいです。

ローカルでは以下のように設定するといいです。

```bash:.bash_profile
if which mint >/dev/null; then
  export MINT_PATH="${HOME}/.mint/lib"
  export MINT_LINK_PATH="${HOME}/.mint/bin"
fi
```

## 注意：Xcode 11.5以上のRun Script Phaseで使うにはワークアラウンドが必要

Xcode 11.5以上のRun Script PhaseでMintを使う場合、以下のビルドエラーが発生します。
<img width="571" alt="スクリーンショット_2020-07-19_15_40_17_skitch.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/c055d89d-cec9-987a-19bd-edd52375a295.png">

~~スクリプトの先頭で `unset SDKROOT` を実行すると回避できますが、副作用があるかもしれません。~~
やはり副作用がありました。R.swiftをMintで管理している場合、「Missing value for `SDKROOT` 」エラーが発生しました。
MintでビルドするコマンドラインツールはmacOSで実行するため、 `mint run` の直前に `xcrun --sdk macosx` を付けて明示的にSDKを指定するのがよさそうです。
![スクリーンショット_2020-11-24_15_16_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/ec7c7eeb-c463-dc42-ac77-88405a513308.png)

Mint + Carthage + XcodeGenを使っている場合、以下のように設定します。
Carthageで管理しているライブラリの種類によっては不要なので、上記のエラーが発生したら対応してもいいかもしれません。

```yaml:project.yml
options:
  carthageExecutablePath: xcrun --sdk macosx mint run Carthage/Carthage carthage
```

__参考__

- https://github.com/yonaskolb/Mint/issues/179
- https://twitter.com/the_uhooi/status/1331040735391404032?s=20

## おわりに

Swift製コマンドラインツールを簡単に管理することができました！
iOSアプリ開発では、SwiftLintやCarthage、XcodeGen、Mockoloなどを管理するといろいろ捗ります。

## 参考リンク

- [yonaskolb/Mint: A package manager that installs and runs Swift CLI packages](https://github.com/yonaskolb/Mint)
- [Mint で Swift 製のコマンドラインツールを管理する - Qiita](https://qiita.com/usamik26/items/be813a224a2146daffb7)
- [Mintについて調べてみた - ryokosuge.io - Medium](https://medium.com/ryokosuge-io/mintについて調べてみた-af180ec45d67)
- https://twitter.com/giginet/status/1219580555244625921?s=20