作成: 2020-04-05T15:54:26+09:00

HaskellプログラムをGitHub Actionsでビルドしてクロスプラットフォーム向けにバイナリをReleaseにアップロードする

やりたかったこと Linuxで開発しているのでMacやWindowsをいちいち起動するのは面倒なので, tag付けしてpushしたら自動的にReleaseにクロスコンパイルしたバイナリをアップロードして欲しい. CircleCIでも良かったかもしれませんが, これまでGitHub Actionsを使ったことが無かったのでとりあえず使ってみたかったのです.

workflowsファイル name : Main on : push jobs : build : strategy : matrix : os : [ ubuntu-latest , macOS-latest , windows-latest ] runs-on : ${{ matrix.os }} steps : - uses : actions/checkout@v2 - if : runner.os == 'Windows' # 何故かWindowsだけキャッシュがダウンロードされないことがある(不確定) uses : actions/cache@v1 with : path : ~\AppData\Local\Programs\stack key : ${{ runner.os }}-stack-${{ hashFiles('**/stack.yaml.lock') }}-${{ hashFiles('**/package.yaml') }} restore-keys : | ${{ runner.os }}-stack-${{ hashFiles('**/stack.yaml.lock') }}- - if : runner.os != 'Windows' uses : actions/cache@v1 with : path : ~/.stack key : ${{ runner.os }}-stack-${{ hashFiles('**/stack.yaml.lock') }}-${{ hashFiles('**/package.yaml') }} restore-keys : | ${{ runner.os }}-stack-${{ hashFiles('**/stack.yaml.lock') }}- - uses : mstksg/setup-stack@v2 - if : runner.os == 'Linux' run : | stack install hlint hlint . - run : stack build - run : stack install --local-bin-path dist - if : runner.os != 'Windows' run : | cd dist zip homura-stopwatch-release-binary-${{ matrix.os }}.zip * rm homura-stopwatch - uses : actions/upload-artifact@v1 with : name : ${{ matrix.os }} path : dist - uses : softprops/action-gh-release@v1 if : startsWith(github.ref, 'refs/tags/') with : files : "dist/*" env : GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}

pushした時毎回実行する 最初はpushした時versionタグが付いているときだけ実行しようと思ったのですが, 何故かトリガーが引かれなかったのと(今考えてみるとworkflow追加前のtagだと引かれないのも当然か), buildエラーはバージョン付ける前に発見してもらいたいのでビルド自体は毎回行ってアップロードはtagがあるときだけ行うようにしました.

WindowsだとStackのディレクトリ位置が異なるのでキャッシュ時に注意 Linux, MacだとStackのpathは ~/.stack ですが, Windowsだと ~\AppData\Local\Programs\stack なので, ifを使って分けて実行する必要があります. yarn cache dir のようなキャッシュディレクトリを出力するコマンドはStackには見つからなかったので, 手で分岐します.

Windowsだとキャッシュが見つからないことがあります 絶対キャッシュが保存されたはずなのに次のビルドでキャッシュが見つからないことがWindowsでのみ発生しました. その次の次ぐらいのビルドだとダウンロードされたので, 反映が遅いのかもしれません.

バイナリが置かれる場所がよく分からない プラットフォームごとに置かれる場所把握するのだるすぎる. stack install --local-bin-path dist でワーキングディレクトリに置くことで解決.

Releaseのassetに同じ名前が使えないのでzipにしてしまう Windowsだと実行ファイルには .exe 拡張子が付きますが, LinuxとMacだと実行ファイル名は同じになります. 同じ名前のファイルはReleaseにアップロード出来ません. (node:1110) UnhandledPromiseRejectionWarning: HttpError: Validation Failed: {"resource":"ReleaseAsset","code":"already_exists","field":"name"} とエラーになります. 実行バイナリファイルの名前をプラットフォームごとに分けることも考えましたが, コマンド名が変更になってしまうのでそれはイヤですね. 仕方ないのでzipに単体ファイルを格納することにしました. zipファイルにプラットフォーム名を入れます. 単体ファイルは削除. サムライズムに怒られそう. zipファイルをお送り頂いた場合の手数料について | 株式会社サムライズム まあここで書かれているzipファイルの問題点は 文字コードが違う → 解凍ターゲットと同じプラットフォームでアーカイブしているので差異が発生しにくい

ディレクトリ構成 → 単体ファイルなので関係ない

パスワード → かけてない ので発生しないので問題ないと判断しました. tarの方が良いかと思いましたが, tar.zst が受け取り先のmacで対応されているのかよくわからないので, tar.xz を使っても単体ファイルとか大して圧縮されないし単にzipで良いと判断しました. GitHub ActionsのWindowsはtarとか使えるようなのでzipも普通に使えると思ってzipをさせてみましたが, 謎にエラーになりました. 解決しようかと思ったのですが, Windowsは元よりexe拡張子で名前が分けられているのでアーカイブする必要も無いかと判断してそのままにすることにしました. 7zipを使えば良いのかなと思いましたが, GitHubホストランナーにインストールされるソフトウェア - GitHub ヘルプ によるとMacには入ってないらしいですね. やっぱりzipはクソだな!(結論)