GitHub Actions で mkr を使う

mkr とは何ですか? MackerelCLI です.

GitHub Actions 上で mkr をセットアップする setup-mkr アクションを作りました.

github.com

こういう感じで uses: susisu/setup-mkr@v1 と一行書くだけで mkr コマンドが使えるようになります.

steps:
- uses: susisu/setup-mkr@v1
- run: mkr org
  env:
    MACKEREL_APIKEY: ${{ secrets.MACKEREL_APIKEY }}

mkr の詳しい使い方はここでは紹介しませんが, mkr throw で何らかのメトリックを投稿したり, mkr wrap でジョブの成否を監視したり, mkr annotations create でイベントを記録したりといったことに利用できるかなと思います.


さてここからは setup-mkr をどう作ったか, あるいはこの手のアクションはどう作れば良いかについて見ていきましょう. setup-mkr のソースコードは 100 行弱しかないので, こちらを読んでも簡単に理解できるのではないかなと思います.

setup-mkr でやること

mkr のリリースにはいくつかの環境向けにビルド済みの実行ファイルを含んだアーカイブがアップロードされているので, これを使うことにします. 逆に言えばここにアップロードされていない環境にはとりあえず対応しないという方針です.

setup-mkr がやることのアウトラインを書いてみると以下の通り.

  1. 使用するバージョンを決定する
  2. アーカイブをダウンロードする
  3. アーカイブを展開する
  4. 実行ファイルのあるディレクトリを PATH に追加する

これらを順番に見ていきましょう.

1. 使用するバージョンを決定する

まずはどのバージョンのリリースからアーカイブをダウンロードするかを決定します. ここでやることを素朴に書き出すと,

  • ユーザーからバージョンの指定があればそのバージョンを選ぶ
  • ユーザーからバージョンの指定があればなければ最新のバージョンを選ぶ

となりそうです.

ところが「最新のバージョンを選ぶ」というのが意外と一筋縄ではいきません. というのも, 都合よく最新バージョンとはどのバージョンなのかを取得しにいく先がないのです.

  • リポジトリ内のファイルは, 一般にはリリースされている最新のバージョンのものとは限らない
  • リポジトリの最新のリリースは, 一般には最新のバージョンのものとは限らない
  • リポジトリの全てのリリースを毎回列挙して, さらにそこから最新バージョンを見つける処理を自前で実装するのはやや大変
  • その他に簡単に取得できて, 最新バージョンを指し続けるようなデータはない

setup-mkr の最初のバージョンでは最新のリリースを使うことにしていましたが, とりあえずは動きはするもののなんとも微妙...

ということで都合よく最新バージョンを取得しに行く先を作ってしまいます.

@actions/tool-cache パッケージには, 特定のフォーマットで作ったバージョンのリスト versions-manifest.jsonリポジトリから取得し, バージョンの選択まで行うことができる機能があるので, これに乗っかることにしましょう. この機能はドキュメントには書かれていませんが, actions/setup-go で使われているのを見つけました.

使い方は簡単で, getManifestFromRepo()リポジトリに配置されたリストを取得してから, findFromManifest() でバージョン指定に従ってバージョンを選択するだけです. ついでに ^1.2.3 のような semver の範囲指定もできるようになったり, 実行環境の情報を元にこの後ダウンロードするアーカイブを絞込むところまでやってくれてお得.

import * as tc from "@actions/tool-cache";

const manifest = await tc.getManifestFromRepo("susisu", "mkr-versions", token, "main");
const release = await tc.findFromManifest(version, true, manifest);

バージョンのリストとそれを生成するスクリプトmkr-versions という別のリポジトリに分けました. setup-mkr のリポジトリ内に配置しても良いのですが, こちらにリスト更新のコミットログが延々積み重なってもあまり嬉しくないので...

リストの更新については, 日次で mkr のリリースを確認して, 更新があれば PR が作られるようにアクションを仕込んでいます. ここに PR をマージするという手作業が残っていますが, 壊れたファイルが作られていないかの確認もしたいのでひとまず良しとしましょう.

2. アーカイブをダウンロードする

上の手順でどのアーカイブをダウンロードすればよいかまでは絞り込めたので, 続いてはそれをダウンロードしてきます.

とはいえこれは @actions/tool-cachedownloadTool() というユーティリティが用意されているので, それを使うだけで完成です.

const file = release.files[0];
const filePath = await tc.downloadTool(file.download_url);

3. アーカイブを展開する

これまた @actions/tool-cache のユーティリティを利用するだけです.

const extractedPath = await tc.extractTar(filePath);

extractTar() の他にも extractZip(), extract7z(), extractXar() が用意されているので, アーカイブの種類に応じて使い分けましょう.

4. 実行ファイルのあるディレクトリを PATH に追加する

これも難しいことはなくて, @actions/coreaddPath() という関数があって環境の違いを吸収してくれるので, ここに実行ファイルのあるディレクトリを指定すれば良いだけです.

import * as core from "@actions/core";

// binDirPath はアーカイブを展開したパスなどから作る
core.addPath(binDirPath);

ということでこれで完成です. 難しそうなことは大体ライブラリが既に解決していて便利ですね.

おまけ: キャッシュ

ところで @actions/tool-cache はその名の通りキャッシュのための機能も備えています. とはいえこれは任意のアクションの実行間でキャッシュが利用できるといったものではなく, アクションを実行するホスト上のキャッシュを利用できるという機能なので, GitHub でホストされている環境に最初からキャッシュが準備されているような有名どころのツールか, self-hosted runner を利用しない限りはあまり役に立つことはないでしょう.

使い方は簡単かつ損することはたぶんないので, まあ使っておいても良いでしょう. 詳しくは @actions/tool-cache の README に書かれているのでここでは割愛します.

おまけ: setup-mkr 以外の方法

GitHub Actions で UbuntumacOS のような環境しか使わないのであれば, mkr の README に書いてあるような方法でセットアップするのは簡単だし, これでも良いのでは? はい良いと思います.

強いて setup-mkr の利点を挙げるとすれば,

  • 環境に依らずに簡単に使える
  • 任意のバージョンが指定できる

あたりでしょうか. あまり機能的な差別化はできていないですが, 素振りがしたかっただけなのでまあ良しとします.

まとめ

  • setup-mkr を作りました
  • 上で紹介したような方法で, この手のツールをセットアップするアクションは簡単に作れます
  • とにかく @actions/tool-cache が便利. これだけ覚えて帰ってください