この投稿は、 Go4 Advent Calendar 2019の 13日目のものになります。
はじめに
例えばWebアプリケーションを開発する際などに、ソースコードの変更を検知して自動で再ビルドをしアプリケーションを起動する、所謂ホットリロードを行う機能はだいたいのメジャーな言語で何かしらの形で提供されていると思います。
Goにもいくつかのライブラリが存在していて、おそらくメジャーなものといえば fresh と realize の2つだと思います。
僕自身もfresh
には数年ほどお世話になっており、実際にプロジェクトの開発でも使用しています。
しかしながら、fresh
を使用していく中でこのライブラリがいくつかの問題を抱えていることもわかりました。
- メンテナンスがされていない
-
mod=vendor
のような仕組みに対応しきれていない (https://github.com/gravityblast/fresh/issues/99) - 監視ファイル(ディレクトリ)の細かい設定ができない
- 起動時の最初のbuildに失敗すると監視が動かない
realize
もこのうちいくつかの同様な問題を抱えており、特に監視ファイルの柔軟な設定ができないのが比較的大きいプロジェクトを効率よく開発していく上でストレスになりました。
また、ローカルの開発環境を Docker
や docker-compose
で構築することは最近では一般的な手段の一つだと思います。その際 Mac
で Docker
を利用する場合は、 Docker for Mac
を利用することが多いと思われますが、処理が遅くホットリロードを行うと応答しなくなることも稀によくあり、これもまた開発効率を落とす要因であることに気づきました。
さきほど紹介したライブラリは、メンテナンスされていないこともありこれらの問題に対する解決をライブラリ側に望むことはほぼ不可能だと考えています。
そのためこれらの代替手段を探していましたが、なかなか見つからなかったため今回は実装することにしました。
本記事は僕が開発しているホットリロードツール fresher
についての紹介記事になります。
アドベントカレンダー投稿日にリリースが間に合わずまだ開発中ではありますが、https://github.com/kanataxa/fresher というレポジトリで公開しています。
realize
ほど多機能でないシンプルなホットリロードを提供するライブラリですが、 fresh
が抱えている課題を解決した better-fresh
を目指しています。
※開発途中のため仕様変更が入る場合があります、ご了承ください
fresher(開発中)が満たす機能について
後発である fresher
を多くのユーザに使ってもらうためには、 今まで存在していた従来のツールと比較した際に優位な点を示すこと、そして学習コストを低くし乗り換えをしやすくする必要があると考えました。
そこで、 fresher
では以下のような機能を提供します。
YAMLベースの設定ファイルによる機能のカスタマイズ
fresher
では細やかな設定をしてくためにユーザ側に設定ファイルを書くこと要求します。
もし手っ取り早く go run main.go
をホットリロードで起動したい場合は以下のような設定を書いて fresher -c your/conf/path.yml
を実行するだけです。
path:
- .
この設定ファイルをユーザがカスタマイズすることで適用するプロジェクトへより最適化できる機能を提供することを目指しています。
ホットリロード
fresher
の一番核となる機能です。
fresher
では, https://github.com/fsnotify/fsnotify を用いてファイルの監視をしています。
fresher
はファイルの作成も検知しなければならないため、監視対象のファイルではなくディレクトリ丸ごとを監視します。
そして fsnotify
によって通知されたファイルが監視対象であった場合に、再度 go build
し、ビルドしたバイナリを実行することでホットリロードを実現しています。
go run
を使用していない理由は、後述のビルドする環境と実行環境を分けるような機能を提供する都合と、go run
内で作成されたバイナリを実行するプロセスが別で起動してしまうため、別途そのプロセスを kill
しなければならないためです。
監視ファイルの設定に柔軟性を持たせる
一番最初に述べたように fresh
は監視するファイルやディレクトリの指定に柔軟性がないと考えています。
例えば、特定のディレクトリの配下の特定のファイルパターンを無視したいときなど、これを指定する方法がありませんでした。
そこで fresher
ではより柔軟に監視ファイルの設定をできるようにします。
具体的には下記の仕様をサポートします。
- ディレクトリごとに監視/無視するファイルやディレクトリを指定できるようにする
- グローバルに無視するファイルやディレクトリを指定できるようにする
- パターンマッチングを用いて指定できるようにする
まずは以下のディレクトリ構成と yml
ファイルをみてください
bash-3.2$ tree ./
./
├── config.yml
├── main.go
├── controller
│ ├── controller.go
│ └── const.go
├── model
│ ├── message.go
│ └── message_test.go
└── pkg
├── config.go
├── const.go
├── pkg.go
└── utils
├── utils.go
└── utils_test.go
path:
- .
- name: controller
exclude:
- const.go
- model
- name: pkg
include:
- utils*
- config.go
exclude:
- vendor
- '*_test.go'
path
というフィールドでディレクトリを指定し include
と exclude
によってそのディレクトリ配下にあるファイルやディレクトリの監視/無視の設定をします。もし include/exclude
を特に指定する必要がない場合は - dir_name
のような記述も可能になっています。
また path
と同じレベルにある exclude
によってグローバルに無視するファイルやディレクトリの設定ができます。
基本的に以下のようなルールで設定ファイルから監視対象のファイルを決定します。
-
path
に記述されたディレクトリを起点に再帰的に監視対象のファイルかどうかを判断する -
include
が指定されている場合は、再帰的に見るディレクトリや監視対象とするファイルをinclude
で指定されたものと一致するもののみにする. もしくはexclude
に設定されているファイルと一致した場合は監視対象から除外する。 -
include/exclude
はファイルかディレクトリかを区別せずfilepath.Match
を用いて一致したものを監視/除外する。
クロスコンパイル可能
Goはクロスコンパイル可能な言語です。そこで fresher
でもクロスコンパイルをサポートします。
env
というフィールドを用いて GOOS
や GOARCH
を指定できるようにします。
もしユーザが fresher
を実行している環境と別の環境のバイナリをビルドしたい場合はこのフィールドを設定することでビルドが可能になります。
また arg
や output
, target
を指定することでビルド対象やバイナリの出力先、ビルド時の引数も指定することが可能です。
build:
env:
- GOOS=linux
target: main.go
output: /tmp/app
arg:
- -v
ビルド前後でのフックの提供
fresher
では build
の直前と直後にユーザが任意の操作をできるような機能を提供しています。これは realize
が提供していた scripts
に似たような機能です。
build
内の before/after
で実行する操作を設定することができます。 async
でその実行処理の完了を待つかどうかを設定することが可能です。
build:
before:
- echo Before Build hook
after:
- docker cp /tmp/app container:/tmp/app
- name: docker
arg:
- exec
- container
- /tmp/app
async: true
ホスト上でビルドし、Dockerで実行する(開発中)
例えば、Mac
で Docker
を使用する際に Docker For Mac
使うことがあるかと思いますが、非常に動作が遅く重いです。
この場合、ホットリロードを Docker
側で行おうとすると、ホスト上で数秒でビルドできるようなソースコードですらビルドにものすごく時間がかかる場合があります。これは開発サイクルに大きな支障をきたしストレスの原因になります。
そこで、 fresher
ではホスト上でビルドしDocker側で実行できるような仕組みを提供することでより快適な開発環境をユーザに提供します。
※この仕組みは現在開発中です。
おわりに
この fresher
は、ユーザの求める機能をなるべくシンプルでより使いやすく提供するということを目指しながら開発しています。
また、Go
のホットリロードツールの新たな選択肢の一つになればよいなと考えています。
まだ開発途中ではあるものの、おおよその機能は開発完了で設定ファイルの書き方をよりブラッシュアップしていくという状態で、年内~年始あたりにv0.0.1
をリリースできればよいなあと考えています。
fresh
が提供していた基本的なホットリロードの機能はすでに実装されていて、fresh
相当のツールとしては利用可能だと思います。
特に監視ファイルの細かい指定ができないことは fresh
を使用していて課題に感じていたので、もし同様の思いを抱えているがいたらぜひ使っていただけると嬉しいです。
また、Docker For Mac
などを用いて開発していて再ビルドのストレスに悩まされている方、まだ開発中ですが、なるべく早く(遅くても年内には)この問題への解決策の一つを fresher
が提供する予定なので提供した際には良ければ利用してみてください。
そして、もし使用するうえで要望や問題があった場合はIssueを立ててください、日本語で大丈夫です。なるべく早いメンテナンスをします。