nest | artifact bundleを用いてSwift製のCLIツールを爆速でインストールする
ざっくり要約
Swift製のCLIツールをインストールする方法として nestを開発しました。
このツールはGitHub Releaseにartifact bundleがあればそれをダウンロードしてインストールします。
もしGitHub Releaseにartifact bundleがなければリポジトリをクローンしてビルドしてインストールします。
artifact bundleがある場合、mintなどの他のツールと比べて、ビルドが不要なのでインストールが早くなります。
swiftlintの場合、4分ほどかかっていたイントールの時間が、わずか3秒になります。
$ nest install realm/SwiftLint
📦 Found an artifact bundle, SwiftLintBinary-macos.artifactbundle.zip, for SwiftLint.
🌐 Downloading the artifact bundle of SwiftLint...
✅ Success to download the artifact bundle of SwiftLint.
🪺 Success to install swiftlint.
はじめに
Swift製のCLIツールをインストールする選択肢として、Mint、Swift Packageのexperimental-installなどが挙げられます。
しかし、これらの方法は全てローカルでビルドすることが前提の設計になっています。
例えばswiftlintをMintでインストールする場合、swiftlintをcloneしてビルドする必要があるのですが、swiftlintのビルドにはとても長い時間がかかります。(自分の手元のmacだと4分弱かかります)
Xcode Cloudのようなキャッシュのコントロールが難しい環境だと、毎回ビルドする必要がありCPU時間を消費してしまうという課題があります。
Artifact Bundle
Swiftにはartifact bundleという仕様が存在します。 (SE-0305)
artifactbundleは複数のアーキテクチャに対するバイナリを仕様通りにまとめたディレクトリ構造です。
そのディレクトリ構造を一つのzipファイルにまとめることで、SwiftPMからbinary targetとして読み込むことができます。
SwiftLint、SwiftGen、SwiftFormatなどはGitHubのリリースでこのartifact bundleを配布しています。
SwiftLintのGitHubリリース
nest
この仕様を利用したnestというツールを開発しました。
このnestはGitHub Releaseにartifact bundleがあれば、それをダウンロードしてインストールします。
もしGitHub Releaseにartifact bundleがなければリポジトリをクローンしてビルドしてインストールします。
artifact bundleがある場合、ビルドするのと比べて、zipファイルをダウンロードして解凍するだけなので、高速にインストールすることができます。
swiftlintの場合、4分かかっていたインストールの時間がたった3秒になります。
仮にartifact bundleがない場合でもクローンしてビルドする他のツールと同様のパフォーマンスを発揮することができます。
またバイナリを直接ダウンロードするのではなく、artifact bundleという形式でダウンロードするのもポイントです。
Xcode CloudなどではまだIntelのmacが動いていますが、artifact bundleの形式でダウンロードすることで、IntelのmacでもApple siliconのmacでも同じインターフェースでパッケージのインストールができ、CPUアーキテクチャの違いを開発者が意識しなくてすみます。
nestの使い方
ここからはnestの使い方を見ていきます。
nestのインターフェースはmintとほぼ同様のものになっています。
nestのインストール
以下のスクリプトを実行することで、インストールすることができます。
curl -s https://raw.githubusercontent.com/mtj0928/nest/main/Scripts/install.sh | bash
このスクリプトは以下のことをします。
- GitHubのリポジトリにあるリリースのartifact bundleをダウンロードする
- そのartifact bundleの中にあるnestを使って、nestをインストールする
- 最初にダウンロードしたnestを削除する
nestを使ってnestをインストールしています。nestがネストしてますね。
こうすることでnestの管理下でnest自身を管理できるようになり、nest自体のアップデートもnestでできるようになります。
インストールされたパッケージは~/.nest/bin
に格納されますので、そちらにPathを通してください。
パッケージのインストール
installコマンドにリポジトリの情報とバージョンを渡すことでインストールできます。バージョンの情報がないときは最新のリリースが使われます。
$ nest install realm/SwiftLint # 最新のリリースがインストールされる
$ nest install realm/SwiftLint 0.57.0 # 0.57.0がインストールされる
$ nest install https://github.com/realm/SwiftLint 0.57.0 # URL形式での指定も可能
また、GitHubのリポジトリを指定する以外にも、artifact bundleのzipのURLを直接指定することも可能です。
$ nest install https://github.com/realm/SwiftLint/releases/download/0.57.0/SwiftLintBinary-macos.artifactbundle.zip
パッケージのアンインストール
uninstallコマンドにアンインストールしたいコマンド名を渡すことでアンインストールできます。バージョン情報が渡されていればそのバージョンだけアンインストールされます。何も渡されなければ全てのバージョンがアンインストールされます。
$ nest uninstall swiftlint # 全てのバージョンのswiftlintがアンインストールされます
$ nest uninstall swiftlint 0.57.0 # 0.57.0のswiftlintだけがアンインストールされます
インストールされているパッケージの一覧を出力する
$ nest list
インストールしたパッケージのバージョンの切り替え
複数のバージョンのパッケージがインストールされている時、switchコマンドでバージョンを切り替えることができます。
$ nest switch swiftlint 0.56.0 # 0.56.0のswiftlintが選択される
nestfile
nestはyaml形式の設定ファイルをサポートして、複数のパッケージの情報をまとめておくことができます。
generate-nestfile
コマンドを使うことで雛形を出力できます。
$ nest generate-nestfile
nestfile.yamlというyamlファイルが生成されるので、そこにパッケージの情報を追加します。
nestPath: ./.nest
targets:
- reference: realm/SwiftLint # リポジトリを指定するだけで最低限の動作をします
- reference: krzysztofzablocki/Sourcery
version: 2.2.5 # バージョン情報
assetName: sourcery-2.2.5.artifactbundle.zip # GitHubのリリースの中のartifact bundleの名前
checksum: 875ef49ba5e5aeb6dc6fb3094485ee54062deb4e487827f5756a9ea75b66ffd8
# Artifact bundleのZIPのURLを直接指定することも可能
- zipURL: https://github.com/SwiftGen/SwiftGen/releases/download/6.6.3/swiftgen-6.6.3.artifactbundle.zip
そしてbootstrapコマンドにこのファイルを渡すことで、複数のパッケージを一括してインストールすることができます。
$ nest bootstrap nestfile.yaml
またyamlの中でnestPath
が指定されている場合、~/.nest/bin
ではなく、nestPath
に指定されたディレクトリの中にbin
ディレクトリが作成され、そこにバイナリがインストールされます
グローバルの環境を汚すことなく、ローカルのプロジェクトに必要なパッケージをインストールできます。
nestfileのメンテナンス
nestfileには上で見たようにバージョン情報やアセットの名前、checksumを記述することができます。
これらはオプションですが、記述することでさまざまなメリットがあります。
例えば、バージョンを記述することで複数人で開発での環境の差を減らしたり、checksumを記述することでzipファイルの改竄を防げたり、などです。
一方、これらをメンテナンスすることは難しくはないですが面倒です。
そこでnestではupdate-nestfile
とresolve-nestfile
の二つのコマンドを提供しています。
update-nestfile
は渡されたnestfileの中のパッケージを全て最新にし、アセット名やchecksumを自動で埋めます。
$ nest update-nestfile nestfile.yaml
resolve-nestfile
はupdate-nestfile
とほぼ同じですが、nestfileの中でバージョンが指定されている時、そのバージョンは更新しません。
$ nest resolve-nestfile nestfile.yaml
先ほど上で定義したnestfileに対して、resolve-nestfile
した結果は以下のようになります。checksumの面倒な計算をしてくれるのが嬉しいです。
nestPath: ./.nest
targets:
- reference: realm/SwiftLint
version: 0.57.0
assetName: SwiftLintBinary-macos.artifactbundle.zip
checksum: a1bbafe57538077f3abe4cfb004b0464dcd87e8c23611a2153c675574b858b3a
- reference: krzysztofzablocki/Sourcery
version: 2.2.5
assetName: sourcery-2.2.5.artifactbundle.zip
checksum: 875ef49ba5e5aeb6dc6fb3094485ee54062deb4e487827f5756a9ea75b66ffd8
- zipURL: https://github.com/SwiftGen/SwiftGen/releases/download/6.6.3/swiftgen-6.6.3.artifactbundle.zip
checksum: caf1feaf93dd32bc5037f0b6ded8d0f4fe28ab5d2f6e5c3edf2572006ba0b7eb
おわりに
新しいnestというCLIツールを開発したので、その紹介でした。
クローンしてビルドするのと比べて爆速でインストールできるので、非常に気に入って使っています。
もしよかったら、使ってみてください。
なぜnestという名前なのか?
Swiftのロゴが鳥なので、その鳥の制作物(artifacts)を保管する場所として巣を意味するnestという名前にしました。
幸運なことに巣に関する絵文字は🪹と🪺の2つがあり、artifactsの有無を絵文字で表現できました。
Discussion