🛠️

go 1.16のgo installを用いてtools.goを削除する

3 min read

今回の記事では、これまで用いられてきたtools.goを振り返りつつ、go 1.16で変更されたgo installを用いてtools.goを削除する方法と、削除して運用してみた感想を記述する。

tools.go

go言語で書かれたアプリケーションを開発する際に、CIや手元のコマンドでコマンドラインツールなどを用いる機会は多い。例えばgolangci-lintなどは多くのアプリケーションで導入されているlinterかと思う。

しかし、go modulesによってバージョン管理されたアプリケーションでは、コードで使われないコマンドラインツールなどのパッケージはgo mod tidyによって消されてしまう。これでは複数人で開発する際に同一の環境を用意できなかったり、一々手作業でバージョン管理をする必要が発生してしまう問題があった。

そこで、これらのツールをmoduleによって管理するために、tools.goを用いる方法が公式のgo wikiでも紹介されている。具体的には、blank importとbuildの制約を用いたファイルを作成するだけである。以下の例は、loggingで有名なzapで使われているtools.goの一部だ。

// +build tools

package tools

import (
	// Tools we use during development.
	_ "golang.org/x/lint/golint"
	_ "honnef.co/go/tools/cmd/staticcheck"
)

ref: https://github.com/uber-go/zap/blob/master/tools/tools.go

これによって通常のビルド時には含まれずgo modulesによってバージョン管理されるという条件を満たすことが出来るため、今でも多くのパッケージで用いられているハックとして使われてきた。

go 1.16になって何が変わったか

go 1.16のRelease Noteには以下のように記述されている。

go install now accepts arguments with version suffixes (for example, go install example.com/cmd@v1.0.0). This causes go install to build and install packages in module-aware mode, ignoring the go.mod file in the current directory or any parent directory, if there is one. This is useful for installing executables without affecting the dependencies of the main module.

ref: https://golang.org/doc/go1.16#go-command

以前からgo installというコマンドがあったが、go 1.16からgo installバージョンを指定することが出来るようになった。シンプルな変更点ではあるが、これによって特定のバージョンを指定してパッケージをインストール出来るようになったため、別途tools.gogo.modによるバージョン管理をする必要がなくなったのである。

更にgo installgo getとは違いバージョンを更新せず、go.modを変更することなくインストール出来るので、能動的にパッケージのバージョンを更新しない限り固定したバージョンを使い続けられる点も便利だ。go 1.16からはgo.modを更新するためのgo get、ツールなどのバイナリをインストールするためのgo install、というようにシンプルに使い分ける意図を感じる。

どのように置き換えるか

先程のzapを例にすると、go.modに記載されている該当のバージョンを参考にgo installすれば良い。例えばMakefileなどでバージョンを指定してgo installし、ツールをインストールするためのコマンドは以下のように書くことが出来る。

TOOLS=\
	golang.org/x/lint/golint@v0.0.0-20190930215403-16217165b5de \
	honnef.co/go/tools/cmd/staticcheck@v0.0.1-2019.2.3

install-tools:
	@for tool in $(TOOLS) ; do \
		go install $$tool; \
	done

そしてtools.goを削除し、go mod tidyすれば必要なくなったパッケージも消すことが出来る。

置き換えてみた感想

正直に言うと、置き換えてすぐに改善されたと感じた点はあまり多くない。強いて言えば/toolsディレクトリを消すことで多少のリファクタリングが出来たくらいか。

逆に、go.modをベースにしたバージョン更新の自動化bot(例えばdependabotなど)に検知されなくなったのは、人によってはデメリットに感じてしまうかもしれないと思った。(弊チームではツール系のパッケージは固定されたバージョンで運用しているため特別マイナスには感じなかった)

しかし、個人的にはtools.goによるバージョン管理はあくまで知る人ぞ知るハックだと思っていて、例えばこれからgo言語を学習していく初学者にとってtools.goによるバージョン管理とgo installによるインストールのどちらが分かりやすいかを考えると、圧倒的に後者の方が分かりやすいのではないかと思う。将来アプリケーション開発に参入する新規Gopherのことを考えると、予め参入障壁を多少下げられる点ではメリットになり得ると思った。

今後のgolangのアップデートやgo wikiなどを注視しつつ、アプリケーションのケースに対応して柔軟に変更していきたい。

Discussion

ログインするとコメントできます