Goでlive reloadingツール作った

GW もうすぐ終わりですね。
今回はコロナの影響で家でゆっくりでした。

今回はタイトルにあるように live reloading するツールを Go で作成しました。
コードを変更したらアプリケーション全体が実行され直すやつです。

github.com

特徴

特徴としては設定ファイルを必要としないことです。
変更を検知する対象のファイルを指定するオプションと除外設定をするオプションがコマンドライン引数として渡せる以外に設定できるオプションはないです。
これは敢えてそうしていて、この後のモチベーションでもふれます。

モチベーション

  • Goでプロジェクト作る時の Go製の live reloading ツールがほしかった
    • live reloading や lint ツールなど本番では使わないツールも バージョン管理もしたい
      • Go だと Go Modules で管理をして renovete とか dependabot でバージョンアップに追従したい
      • こういったツールを Go Modules で管理する方法は公式のWiki が参考になります。
  • 設定ファイルを書くのが面倒だった
    • 世の中には既に go-task/taskoxequa/realize のような Task runner の中に live reloading な機能が組み込まれているものや cosmtrek/air のような live reloading のためのツールなどがあります。他にもあった気がする。
      • すべてのツールではないですが、そのツールを使うために設定ファイルが必要になる場合があります。これ自体はよくて、複数のタスクを定義したり依存関係を表現したりとかゆいところに手が届きそうで便利です。しかし 自分としてはファイルが変更されたら指定したコマンドを再度実行しなおしてくれれば良いだけでした。
  • GW 暇だった
    • 緊急事態宣言によってどこにも出かけられないので家で本読んだりゲームしたりはしたのですが、やっぱりコードが書きたくなった。
    • なんだかんだでこのモチベーションが一番強かったきがするwww

技術的なところ

ファイルの変更検知は fsnotify/fsnotify を使いました。
MacOSWindows など複数の OS に対しての変更検知をサポートしていて便利な感じです。

ファイルの変更検知する watcher とコマンドの実行してくれる executor を用意してそれらの間を go の channel で連携しつつ watcher で変更を検知したら event を channel に送って executor は event を受け取ったらコマンドを実行しなおします。

↓はイメージ図
f:id:hatappi1225:20200506150405j:plain

今回実装の時に工夫が必要だったのは 変更を検知する対象のファイルのパスの指定でした。
例えば Go のプロジェクトとで cmd/server/main.go のようにいくつかディレクトリが掘ってありそのファイルを対象にしたいとします。

その時自分だと **/*.go のように ** を指定していディレクトリを再帰的に参照することを期待します。
実装を始めた時には Go の filepath.Glob を使うことを考えてましたが、 ** に対応してなくて断念しました。

※ まだ閉じてないですが、過去にissueにあがってたようです。 github.com

今回は自分でそれを実装するために正規表現を使うことにしました。
** が指定されたら ([^/]*/)* のような正規表現を使います。
このあたり良い方法が他にあったら知りたい。。。。

最後に

今回は Go で live reloading ツールを作りました。
変に動かないところとかあったら issue や Twitter で教えてもらえると嬉しいです!