Mintについて調べてみた

開発チームでSwiftLintなどのコマンドラインツールをローカルとCIの環境でバージョンを合わせたいよねという話になった時に候補に上がったので調べました


調べたきっかけ

  • SwiftLintなどのコマンドをローカルとCI環境とで差分をなくしたい
  • CI環境でインストールの時間を最短にしたい

と、こんな感じの事があり、Swift製コマンドラインツールのパッケージマネージャーであるMintが候補に上がったので実用可能か調べてみました。


Mintとは

README.mdより
A package manager that installs and runs Swift command line tool packages.

簡単に紹介するとSwift製のコマンドラインツールをインストールして実行できるパッケージマネージャー。

色々使い方を調べたので紹介していきます


Mintのインストール

Homebrewで簡単にinstallします。

> brew install mint
==> Downloading https://homebrew.bintray.com/bottles/mint-0.11.2.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring mint-0.11.2.high_sierra.bottle.tar.gz
🍺 /usr/local/Cellar/mint/0.11.2: 6 files, 10.6MB

それ以外の方法はREADME参照してください。

これで mint コマンドが使えるようになったので早速使用していきます。


MintでSwiftLintをインストールしてみる

コマンドを追加(管理)するのに installbootstrapコマンドがあるのでそちらを紹介します。

installコマンド

バージョン指定しなければ最新版が入ります。

> mint install realm/SwiftLint
🌱 Cloning https://github.com/realm/SwiftLint.git 0.29.1...
🌱 Building SwiftLint Package with SPM...
🌱 Installing SwiftLint...
🌱 Installed SwiftLint 0.29.1
🌱 Linked swiftlint 0.29.1 to /usr/local/bin

バージョン指定する場合はpackage名の後ろに @バージョンをつけます。

> mint install realm/SwiftLint@0.29.1 # 0.29.1が入れたいなら
🌱 Cloning https://github.com/realm/SwiftLint.git 0.29.1...
🌱 Building SwiftLint Package with SPM...
🌱 Installing SwiftLint...
🌱 Installed SwiftLint 0.29.1
🌱 Linked swiftlint 0.29.1 to /usr/local/bin

実行してみます。

> mint run swiftlint
🌱 Using realm/SwiftLint 0.29.1 from Mintfile.
🌱 SwiftLint 0.29.1 already installed
🌱 Running swiftlint 0.29.1...
~SwiftLintのログ~

bootstrapコマンド

Mintfileを用意してインストールしたツールを管理することができます。

> cat Mintfile
realm/Swift@0.291

bootstrapコマンドでインストールします。

> mint bootstrap
🌱 Found 1 package in Mintfile
🌱 Cloning https://github.com/realm/SwiftLint.git 0.29.1...
🌱 Building SwiftLint Package with SPM...
🌱 Installing SwiftLint...
🌱 Installed SwiftLint 0.29.1
🌱 Installed 1 package from Mintfile

実行してみます。

> mint run swiftlint
🌱 Using realm/SwiftLint 0.29.1 from Mintfile.
🌱 SwiftLint 0.29.1 already installed
🌱 Running swiftlint 0.29.1...
~SwiftLintのログ~

installコマンドと bootstrapコマンドで細かい違いがあったのでそちらを紹介していきます。


installとbootstrapの違い

以下の違いがありました。

  • installを使用する際はpackageの指定が必須でbootstrapを使用する際はMintfileが必須
  • Mintfileを使用したmint bootstrapだとlinkされない

細かく書いていきます。

installを使用する際はpackageの指定が必須でbootstrapを使用する際はMintfileが必須

MintfileCocoaPodsなどを使用しているiOSエンジニアには馴染みの深いものかと思います。
しかしmint installの際は個別にinstallする必要があり、mint bootstrapを使用する際はMintfileが必要でした。
まずはmint installのhelpをみてみます。

> mint install --help
Usage: mint install <package> [<executable>] [options]
Install a package. If the version is already installed no action will be taken
The package can be a shorthand for a github repo "githubName/repo", or a fully qualified .git path.
An optional version can be specified by appending @version to the repo, otherwise the newest tag will be used (or master if no tags are found.)
By default all the executable products from the Package.swift are installed. The executable parameter can be used to link just a single executable globally.
Options:
-f, --force Force a reinstall even if the package is already installed
-h, --help Show help information for this command
-m, --mintfile <value> Custom path to a Mintfile. Defaults to Mintfile
-n, --no-link Whether to prevent global linkage
-s, --silent Silences any output from Mint itself
-v, --verbose Show verbose output

次にmint bootstrapのhelpを見てみます。

> mint bootstrap --help
Usage: mint bootstrap [options]
Installs all the packages in a Mintfile
Options:
-h, --help Show help information for this command
-m, --mintfile <value> Custom path to a Mintfile. Defaults to Mintfile
-v, --verbose Show verbose output

mint installのオプションにある-m --mintfileは参照のみでした。
なので

> mint install
Usage: mint install <package> [<executable>] [options]
~ 省略 ~
Error: command requires between 1 and 2 arguments

ということでエラーになりました。

Mintfileを使用したmint bootstrapだとlinkされない

ログを見るとわかるのですが

mint install realm/SwiftLint
🌱 Cloning https://github.com/realm/SwiftLint.git 0.29.1...
🌱 Building SwiftLint Package with SPM...
🌱 Installing SwiftLint...
🌱 Installed SwiftLint 0.29.1
🌱 Linked swiftlint 0.29.1 to /usr/local/bin # link先が指定されている

ですが、bootstrapだと

> mint bootstrap
🌱 Found 1 package in Mintfile
🌱 Cloning https://github.com/realm/SwiftLint.git 0.29.1...
🌱 Building SwiftLint Package with SPM...
🌱 Installing SwiftLint...
🌱 Installed SwiftLint 0.29.1
🌱 Installed 1 package from Mintfile

という形でlinkされていないことがわかります。
例でSwiftLintを削除したりしながら詳しくログを見ていきます。

一旦swiftlintを削除します

> mint uninstall realm/SwiftLint
🌱 realm/SwiftLint was uninstalled

ないのわかってますが、コマンド叩いてみます。

> swiftlint
zsh: command not found: swiftlint

mint installswiftlintをインストールします。

> mint install realm/SwiftLint # 再度install
🌱 MINTFILE: repository realm/SwiftLint has no defined version. Specify a version using <Repo>@<Commitish>.
🌱 Finding latest version of SwiftLint
🌱 Resolved latest version of SwiftLint to 0.29.1
🌱 Cloning https://github.com/realm/SwiftLint.git 0.29.1...
🌱 Building SwiftLint Package with SPM...
🌱 Installing SwiftLint...
🌱 Installed SwiftLint 0.29.1
🌱 Linked swiftlint 0.29.1 to /usr/local/bin, replacing version 0.29.1.

この状態でswiftlintを叩く( /usr/local/binにPATHを通しているので)と実行されます。

> swiftlint # /usr/local/binにPATHが通ってるので使える
Linting Swift files at paths
No lintable files found at paths: '' # .swiftファイルがないので...

今度はbootstrapコマンドからインストールして同じことをやります。

確認のためもう一度アンインストールします。

> mint uninstall realm/SwiftLint
🌱 realm/SwiftLint was uninstalled

コマンドが消されていることを確認します。

> swiftlint
zsh: command not found: swiftlint

今度はmint bootstrapで追加してみます。

> mint bootstrap
🌱 Found 1 package in Mintfile
🌱 Cloning https://github.com/realm/SwiftLint.git 0.29.1...
🌱 Building SwiftLint Package with SPM...
🌱 Installing SwiftLint...
🌱 Installed SwiftLint 0.29.1
🌱 Installed 1 package from Mintfile

コマンドを叩いてもlinkされていないため、実行できません。

> swiftlint
zsh: command not found: swiftlint

mint run swiftlintで実行できます。

> mint run swiftlint
🌱 Using realm/SwiftLint 0.29.1 from Mintfile.
🌱 SwiftLint 0.29.1 already installed
🌱 Running swiftlint 0.29.1...
Linting Swift files at paths
No lintable files found at paths: ''

ということでmint bootstrapでinstallしたやつはmint run packageで実行することになります。

installして使う際の手段の良し悪しがわかったので、CI上で同じコマンドラインツールを同じバージョンで使う際に考えたことをまとめておきます。


CIで使うからキャッシュしたい

Travisで使用する際に毎回before_install or installのhookでmint install realm/SwiftLintって実行するのも勿体ないなと思いました。
mint installが結構時間かかります。体感5分くらい(正確な時間はマシンパワーによるのでわかりませんが)。
なのでmint install swiftlintした際に指定バージョンがあったらinstallしないでない場合だけinstallしたいところです。

README.mdを読み漁ったらその手段がわかったので紹介します。

MINT_PATHとMINT_LINK_PATHを指定する

以下の環境変数を指定すると指定したPATHにpackageとcommandがインストールされます。

  • MINT_PATH: mintのbuildのcacheディレクトリ
  • MINT_LINK_PATH: packageのbuildした結果のcommandのリンク先ディレクトリ

この2つを用いて任意の場所にキャッシュとコマンドをインストールすれば、Travis上でもキャッシュが聞いて無駄なインストール時間を食わなくて済む算段です。

以下ログです。

> mkdir -p mint/{lib,bin}
> tree
.
├── Mintfile
└── mint
├── bin
└── lib
3 directories, 1 file
> MINT_PATH=./mint/lib MINT_LINK_PATH=./mint/bin mint install realm/SwiftLint
🌱 Using realm/SwiftLint 0.29.1 from Mintfile.
🌱 Cloning https://github.com/realm/SwiftLint.git 0.29.1...
🌱 Building SwiftLint Package with SPM...
🌱 Installing SwiftLint...
🌱 Installed SwiftLint 0.29.1
🌱 Linked swiftlint 0.29.1 to /path/to/mint/mint/bin.
> tree
.
├── Mintfile
└── mint
├── bin
│ └── swiftlint -> /path/to/mint/lib/packages/github.com_realm_SwiftLint/build/0.29.1/swiftlint
└── lib
├── metadata.json
└── packages
└── github.com_realm_SwiftLint
└── build
└── 0.29.1
└── swiftlint
7 directories, 4 files

とこんな感じになるので

> ./mint/bin/swiftlint
Linting Swift files at paths
No lintable files found at paths: ''

と実行できるようになります。
Travisで実行するなら

cache:
directories:
- mint
before_install: // or install
- MINT_PATH=./mint/lib MINT_LINK_PATH=./mint/bin mint install realm/SwiftLint
script:
- ./mint/bin/swiftlint

という感じで記述すればいい感じにキャッシュされてうまく使用できるのではないでしょうか。

まとめ

CIでSwiftLintを導入した際に色々あって調べてみました。

Mintに対応しているcommandは多い(Carthageとか)ので、もし複数人チームで「最新版使おうぜ!」なルールで運用している場合は1度検討してみるのもいいかもしれません。