はじめに
書籍 詳解Go言語Webアプリケーション開発 を読んでいたら air というLive Reload のツールが登場しました。
書籍ではハンズオン形式で触ってみただけだったので、もう少し触ってみました。
air を使う
インストール
go instal
で air
のバイナリをインストールします。
% go install github.com/cosmtrek/air@latest
GOPATH/bin
配下にインストールされました。
% go env GOPATH /home/dshimizu/go % which air /home/dshimizu/go/bin/air
ヘルプを見てみます。
% air -h Usage of air: If no command is provided air will start the runner with the provided flags Commands: init creates a .air.toml file with default settings to the current directory Flags: -build.args_bin string (default "DEFAULT") -build.bin string (default "DEFAULT") -build.cmd string (default "DEFAULT") -build.delay string (default "DEFAULT") -build.exclude_dir string (default "DEFAULT") -build.exclude_file string (default "DEFAULT") -build.exclude_regex string (default "DEFAULT") -build.exclude_unchanged string (default "DEFAULT") -build.follow_symlink string (default "DEFAULT") -build.full_bin string (default "DEFAULT") -build.include_dir string (default "DEFAULT") -build.include_ext string (default "DEFAULT") -build.include_file string (default "DEFAULT") -build.kill_delay string (default "DEFAULT") -build.log string (default "DEFAULT") -build.rerun string (default "DEFAULT") -build.rerun_delay string (default "DEFAULT") -build.send_interrupt string (default "DEFAULT") -build.stop_on_error string (default "DEFAULT") -c string config path -color.app string (default "DEFAULT") -color.build string (default "DEFAULT") -color.main string (default "DEFAULT") -color.runner string (default "DEFAULT") -color.watcher string (default "DEFAULT") -d debug mode -log.main_only string (default "DEFAULT") -log.time string (default "DEFAULT") -misc.clean_on_exit string (default "DEFAULT") -root string (default "DEFAULT") -screen.clear_on_rebuild string (default "DEFAULT") -screen.keep_scroll string (default "DEFAULT") -testdata_dir string (default "DEFAULT") -tmp_dir string (default "DEFAULT") -v show version
たくさんのオプションがありました。
-v
でバージョンが出てくるかと思いましたが出てきませんが一旦スルーします。
% air -v __ _ ___ / /\ | | | |_) /_/--\ |_| |_| \_ , built with Go
起動してみる
適当にプロジェクトディレクトリを作成します。
% mkdir workspace/air-sample % go mod init air-sample
適当に以下のような main.go
を作成します。
package main import ( "net/http" ) func main() { server := http.Server{ Addr: "127.0.0.1:8080", Handler: nil, } server.ListenAndServe() }
air
コマンドを実行してみます。
% air __ _ ___ / /\ | | | |_) /_/--\ |_| |_| \_ , built with Go watching . !exclude tmp building... running...
正常に実行できました。
この時に、上述で作成した main.go
がビルドされて起動します。
デフォルトだと、プロジェクトディレクトリ配下の tmp/
ディレクトリ(今回だと ari-sample/tmp
)にバイナリが配置されて、これが実行されます。
下記のような感じでプロセスが起動されていることが確認できます。
% ps auxf | grep main dshimizu 1467716 0.0 0.0 4504 712 pts/0 S+ 07:36 0:00 | \_ grep --color=auto main dshimizu 1467699 0.0 0.0 2484 516 pts/4 Ss+ 07:36 0:00 \_ /bin/sh -c /home/dshimizu/workspcace/air-sample/tmp/main dshimizu 1467700 0.0 0.1 1084628 4972 pts/4 Sl+ 07:36 0:00 \_ /home/dshimizu/workspcace/air-sample/tmp/main
air
コマンドを実行しているターミナルはこのままにして、並行して main.go
を、下記のような形で更新してみます。
package main import ( "net/http" ) type HelloHandler struct{} func (h HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World")) } func main() { hello := HelloHandler{} server := http.Server{ Addr: "127.0.0.1:8080", Handler: nil, } http.Handle("/hello", hello) server.ListenAndServe() }
この時、 air
コマンドを実行しているターミナルで、ファイルを変更して保存するたびに、自動でビルドし直されます。
その際、ファイルが更新されるたびに下記のような出力がなされます。ファイルに文法エラー等があるとエラーが出力されますが、正常な状態になれば、 main.go has changed
,
building...
, running...
という出力になり、最新の状態で自動で起動し直されていることが確認できます。
main.go has changed building... # ari-sample ./main.go:8:11: undefined: HelloHandler failed to build, error: exit status 1 : main.go has changed building... running...
プロセスが再起動されていることが確認できます。
% ps auxf | grep main dshimizu 1467716 0.0 0.0 4504 712 pts/0 S+ 07:39 0:00 | \_ grep --color=auto main dshimizu 1467699 0.0 0.0 2484 516 pts/4 Ss+ 07:39 0:00 \_ /bin/sh -c /home/dshimizu/workspcace/air-sample/tmp/main dshimizu 1467700 0.0 0.1 1084628 4972 pts/4 Sl+ 07:39 0:00 \_ /home/dshimizu/workspcace/air-sample/tmp/main
設定ファイルを作成/変更してみる
プロジェクト直下に .air.toml
ファイルを配置して、デフォルトの挙動を変更できます。
下記のコマンドで、.air.toml
を生成できます。
% air init
v1.42 の air だと、下記のような内容のものが生成されます。
main.go
の配置先等に応じて cmd
や bin
あたりを変えたり、除外したいもの、含めたいものに応じて exclude_xxx
, include_xxx
あたりを変更することになりそうです。
root = "." testdata_dir = "testdata" tmp_dir = "tmp" [build] args_bin = [] bin = "./tmp/main" cmd = "go build -o ./tmp/main ." delay = 0 exclude_dir = ["assets", "tmp", "vendor", "testdata"] exclude_file = [] exclude_regex = ["_test.go"] exclude_unchanged = false follow_symlink = false full_bin = "" include_dir = [] include_ext = ["go", "tpl", "tmpl", "html"] include_file = [] kill_delay = "0s" log = "build-errors.log" rerun = false rerun_delay = 500 send_interrupt = false stop_on_error = false [color] app = "" build = "yellow" main = "magenta" runner = "green" watcher = "cyan" [log] main_only = false time = false [misc] clean_on_exit = false [screen] clear_on_rebuild = false keep_scroll = true
試しに go build
せずに go run
で動くかをやってみました。
@@ -4,8 +4,9 @@ [build] args_bin = [] - bin = "./tmp/main" - cmd = "go build -o ./tmp/main ." + #bin = "./tmp/main" + #cmd = "go build -o ./tmp/main ." + cmd = "go run main.go" delay = 0 exclude_dir = ["assets", "tmp", "vendor", "testdata"] exclude_file = []
running...
の出力が出ませんでしたが、プログラムは起動しているようでした。
なお、デフォルトで .air.toml
を読み込んでくれるようですが、 air -c ファイル名
のコマンドでファイル名を指定することもできるようです。
% air __ _ ___ / /\ | | | |_) /_/--\ |_| |_| \_ , built with Go watching . !exclude tmp building...
プロセスは下記のような状態でした。
% ps auxf | grep main dshimizu 1468191 0.0 0.0 2484 572 pts/2 Ss+ 08:07 0:00 | \_ /bin/sh -c go run main.go dshimizu 1468192 0.0 0.6 1240428 26156 pts/2 Sl+ 08:07 0:00 | \_ go run main.go dshimizu 1468231 0.0 0.3 1010640 13216 pts/2 Sl+ 08:07 0:00 | \_ /tmp/go-build692725095/b001/exe/main dshimizu 1469281 0.0 0.0 4504 644 pts/3 S+ 08:14 0:00 \_ grep --color=auto main
main.go
を変更してみました。
package main import ( "net/http" ) type HelloHandler struct{} func (h HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World!!")) // !! をつけた } func main() { hello := HelloHandler{} server := http.Server{ Addr: "127.0.0.1:8080", Handler: nil, } http.Handle("/hello", hello) server.ListenAndServe() }
ファイルの更新は検知してくれたようで、ここでは building...
, running...
と出力はされました。
% air __ _ ___ / /\ | | | |_) /_/--\ |_| |_| \_ , built with Go watching . !exclude tmp building... main.go has changed main.go has changed building... running...
しかしプロセスの状態は変更ないようでした。
% ps auxf | grep main dshimizu 1468191 0.0 0.0 2484 572 pts/2 Ss+ 08:07 0:00 | \_ /bin/sh -c go run main.go dshimizu 1468192 0.0 0.6 1240428 26156 pts/2 Sl+ 08:07 0:00 | \_ go run main.go dshimizu 1468231 0.0 0.3 1010640 13216 pts/2 Sl+ 08:07 0:00 | \_ /tmp/go-build692725095/b001/exe/main dshimizu 1469281 0.0 0.0 4504 644 pts/3 S+ 08:14 0:00 \_ grep --color=auto main
リクエストを投げてみましたが、 Hello World!!
とはなっていませんでした。go run
の場合は Live Reload はされてないようです。
% curl localhost:8080/hello Hello World%
まとめ
- Go の Live Reload を実行してくれる air を使ってみました
- air コマンド実行時に、アプリケーションをビルドして実行し、ファイルの更新が発生した場合に自動で検知してアプリケーションが再起動されることが確認できました
- 設定ファイルは
.air.toml
で、デフォルトではこのファイルを読み込んでくれるようでした - リロード時には
go build
されるようなので、これができる必要があるようでした