良さそうな Swift 製のコマンドラインツールをあったとして、それを CI 上でだけで動かすだけなら極力ビルド済みのバイナリで動かしたい(円安ですし)。artifactbundle に対応すればそれが実現できるよというお話。artifactbundle の詳細は SE-0305: Package Manager Binary Target Improvements を読むといいんだけど、自分の理解としても残しておきたい。
arifactbundle とは
artifactbundle はマニフェストファイル( info.json
)とアーティファクト(実行可能ファイル)を拡張子 .artifactbundle
でまとめたもの。
artifactbundle は Package.swift で以下のように binaryTarget
にビルド済みの実行ファイルが含まれた zip ファイルの URL とその checksum を指定して、Swift Package Plugin を通して利用することができる。
.binaryTarget( name: "your-tool", url: "....", checksum: "...." )
前述した CI 上でのビルドの時間はもちろん、Build Tool Plugin の prebuildCommand
だとそもそも実行がプラグイン自体のビルド前に行われるためそこで利用するツールはビルド済みである必要があるのでそういった問題も解決できる。もともと SPM の binaryTarget
は XCFramework 形式のみサポートされていたんだけど、SE-0305 で .arifactbundle
を含む zip ファイルへの URL 参照を許可できるようにしたという経緯。zip を解凍した時に .xcframework
がなければ .arifactbundle
をみてくれるようになっているっぽい。
ということで、さっそく作ってみる。
arifactbundle の作り方
実際にビルド済みバイナリを使える状態にするまでにやることとしてはおおまかに以下の通り。
- 利用したい環境向け(macOS, Linux, Windows etc)のビルドを作成する
- macOS:
swift build --configuration release --arch arm64 --arch x86_64
- Linux:
swift build --configuration release --static-swift-stdlib
- Windows:
swift build --configuration release -Xswiftc -gnone
- 参考: SwiftFormat
- macOS:
- ビルドで得たバイナリとマニフェストファイルを
.artifactbundle
ファイルにまとめる .artifactbundle
をzip形式で圧縮して GitHub のリリースの Assets にアップロードする- Zipアーカイブの Checksum を取得し GitHub のリリース等に併記する
.artifactbundle
ファイルは以下のような構造。
<name>.artifactbundle ├ info.json ├ <artifact> │ ├ <variant> │ │ ├ <executable> │ │ └ <other files> │ └ <variant> │ ├ <executable> │ └ <other files> ├ <artifact> │ └ <variant> │ ├ <executable> │ └ <other files> │ <artifact> ┆ └┄
そこに含まれる info.json というマニフェストファイルには実行可能ファイルのパスと対応するプラットフォームを示す supportedTriples
を羅列した情報を含める。以下は macOS のみ対応した場合:
{ "schemaVersion": "1.0", "artifacts": { "your-tool": { "version": "__VERSION__", "type": "executable", "variants": [ { "path": "your-tool/bin/your-tool", "supportedTriples": ["x86_64-apple-macosx", "arm64-apple-macosx"] } ] } } }
この形式の json ファイルをテンプレートファイル( .template
)としておき、バージョン情報などを書き換えた上で最終的に info.json として .artifactbundle
ファイルに含まれるようにする。
// .artifactbundle の箱を用意 mkdir your-tool.artifactbundle // LICENSE などバンドルに含めたいファイルはコピーしておくみたいなことはご自由に cp LICENSE your-tool.artifactbundle // バージョンを書き換えた上で info.json を .artifactbundle の中に含める sed 's/__VERSION__/'"v1.0.0"'/g' artifact-bundle-info.template > "your-tool.artifactbundle/info.json"
そして swift build
でできた実行ファイルを .artifactbundle
ファイルの中にコピーしていく。追加する場所は先ほどの info.json の中で指定していた path
の箇所。ビルドでできた実行ファイルは環境によって生成される場所が違うので注意が必要。以下も macOS に対応する場合:
mkdir -p your-tool.artifactbundle/your-tool/bin # macOS のビルドは .build/apple/Products/Release 配下 cp .build/apple/Products/Release/your-tool your-tool.artifactbundle/your-tool/bin
ここまでのステップで .artifactbundle
ファイルが作成できる。
実際に利用する時は zip ファイルのURLを参照するので、 .artifactbundle
ファイルを Zip アーカイブして GitHub リリースの Artifact にアップロードすればOK。
binaryTarget
で指定する時は前述した checksum の値も必要なので作成した zip ファイルに対して swift package compute-checksum your-tool.artifactbundle.zip
を実行して得られるので、アップロードと同時にリリースに併記すればやることは以上!
リリースフローにここまでの一連の処理の流れを乗せたい場合は、スクリプトを用意したりCIを作り込まないといけないのでもう一踏ん張りが必要。実際に未対応だったOSSにPR出してみたので、 リリースフローを自動化したいという方の参考になれば嬉しいです。