Go(golang)はかなりいろいろなことがよく考えられた良い言語だが、大きな欠陥がある。その欠陥はやはりimport。gitやmercurial等のリモートリポジトリを指定できてそれはそれで大層便利ではあるのだが、タグやブランチ、バージョン等を指定できない(常にmasterのHEADが使われる)ことがしばしば問題となる。
このimport問題の中でもとりわけ大きなものが、local importの問題だ。
リポジトリ上で開発していて、同じリポジトリのサブディレクトリを対象にimportしたいとする。
import "./subdir"
通常はこれで良いのだが、$GOPATH/src以下もgo getでcloneしてくるリポジトリになっていて、ここのソースに対して作業をしていると、相対パスによるimportがエラーになる。
can't load package: /path/to/go/src/github.com/wtnb75/xxxxx/yyyy.go:16:2: local import "./subdir" in non-local package
つまり、$GOPATHにのっとって開発する(実際golangではそれが推奨されている)場合は、こう書かなければならない。
import "github.com/wtnb75/xxxxx/subdir"
それがgithub.com/wtnb75/xxxxxからimportする場合でもそうなのだ。これは時折深刻な問題を引き起こす。xxxxx/subdir以下の関数の引数を変更し、呼び出し元のxxxxxの呼び出し方も追随して変更することを考える。github flowではこのような場合ブランチを切ってcommit, pushし、作業が終了して動くようになったらPull Request(PR)を出し、レビューを受けてマージしてもらう。自分がメンバーに入っていない赤の他人のプロジェクトであればpush先を自分のテンポラリのリポジトリにしてPRを出すし、メンバーに入っていれば元のリポジトリのブランチで作業するわけだ。
このときdroneやtravis(jenkinsも)といったツールを使ってビルド/テストしたり、自分でもローカルでcloneしてビルドするとか、ブランチを切ってテストするといったことをする。$GOPATH以下にpullしてきてテストする場合は良いのだが、別のところにcloneしてしまった場合。このときimport文が元のままだと、この、、、
import "github.com/wtnb75/xxxxx/subdir"
これはmasterのHEADなわけだから、subdir以下のAPIは変更される前であって、呼び出し元はcloneされた新しいものだ。結果として起きるのはビルドの失敗。テストができない。github flowにおいてはmasterは常にデプロイ可能な状態である、という原則がある。そこで質問だが、ビルドもできないものを好んでmasterにマージしたい人はいるだろうか?
だから、私としてはimportは、それが$GOPATH/src以下であっても相対パスで指定できて欲しいと思う。実際go getで取ってきたディレクトリそのままのところでgitで差分を見ながら作業するってのがそれなりに快適なので、それを邪魔するような機能はつけないで欲しいと思う。local import in non-local packageのエラー…それをエラーにする意義はないんじゃないかな。
おそらく現状だとテスト側としては正しいのはgo getで$GOPATH/src以下に持ってきて、git checkoutでブランチを手動で切り替えてテストを続ける、という一手間だろう。go getやgo buildではcloneが起きるときはmasterだけどあと修正されたかどうかは気にしないので、これでうまくいく。
ただ面倒っちゃ面倒だよね。
$GOPATH/src以下でlocal importができるようになれば解決する。