TL;DR
- module 有効下の
go get
は-u
相当 (バージョン未指定なら) - go.mod の
module
には/v{2以上の値}
という suffix でメジャーバージョンを書く必要がある- 省略すると v0 もしくは v1 とみなされる
- 取得時には
go get example.org/foo/bar/v2
のようにする- パッケージを取ってくるときはこれで良いが、コマンドの時はリリース済みのメジャーバージョンを機械的に知る方法がないのでキツいね
髄記
言わずと知れた go get
、Goのパッケージをインストールしたり、コマンドをインストールしてくれたり、バージョン管理してくれる偉いやつ。
調べてみると module が有効かどうかで振る舞いが変わるらしい。
src/cmd/go 下をみると module かどうかでコマンドそのものが入れ替わってる。 internal/get が昔ながらの実装で、internal/modget が module 対応版。
module 対応版は go get
だけかつバージョン指定を省略すると -u
フラグを省略しても自動で upgrade 相当らしい。以下は該当箇所
// If no version suffix is specified, assume @upgrade.
// If -u=patch was specified, assume @patch instead.
if vers == "" {
if getU != "" {
vers = string(getU)
} else {
vers = "upgrade"
}
}
ライブラリ側は v0 と v1 の時は特に気にすることはない。
リリースするたびに普通に semver に従って v1.2.3
みたいにタグを付ければよい。
問題は v2 以降。
go.mod がない時は逆に問題がおこらない。
タグに付けたバージョンに +incompatible
というサフィックスが付いた状態でアクセスできるようになる。
例: v2.3.4
→ v2.3.4+incompatible
使うほうは v2.3.4
を取ってくれば勝手に +incompatible
が付くので気にすることはなにもない。「あ、こいつpinningしてないな(プププ」とでも思っておけばよい。
本番は go.mod がある時。さらに言うと、ある時までは go.mod が無かったけどあるバージョンから追加されたやつ。
たとえば github.com/koron/foobar
というモジュールならばこんな go.mod を書いているかと思う。
module github.com/koron/foobar
ここで v2.3.3
までは go.mod 無しで運用してて v2.3.4
で上記の go.mod を追加したとしよう。
このケースでは残念ながら v2.3.4
は利用できない。一生できない。
仮にバージョン指定で取ってこようとすると以下のようなエラーになる。
$ GO111MODULE=on go get github.com/koron/foobar@v2.3.4
go get github.com/koron/foobar@v2.3.4: github.com/koron/foobar@v2.3.4: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2
なんかエラーメッセージで言ってることがよくわからないが「go.mod に書かれてるメジャーバージョンが v2 じゃないから使えねぇよ」ということらしい。
module github.com/koron/foobar
ってのは特殊でこれは v0 もしくは v1 であると見做されるようになってる。たぶん互換性目的かな。
なので v2.x.x
としてリリースしたいなら go.mod は以下のようにする必要がある。
module github.com/koron/foobar/v2
そのうえで v2.3.5
としてタグを打ちリリースしよう。
こうするとユーザー側は以下のように /v2
サフィックスで取れるようになる。
$ GO111MODULE=on go get github.com/koron/foobar/v2
…パッケージなら納得感あるけど、コマンドだとちょっとキツくね? リリース済みのメジャーバージョンを全てチェックする方法が無い。
proxy.golang.org/github.com/koron/foobar/@v/list
を見ると v0, v1 もしくは go.mod 無し時代のリリースは全部見れる。
以下は mattn/jvgrep における例:
- https://proxy.golang.org/github.com/mattn/jvgrep/@v/list - v0, v1 もしくは go.mod 無しのリリース
- https://proxy.golang.org/github.com/mattn/jvgrep/v5/@v/list -
/v5
で取れそうな v5.x.x のリリース。ただし v5.8.6 以降でないと go.mod が無かったり不正だったりで取れない。
なんか正攻法もしくは回避法ないかもうちょっと探ってみよう。
なお検証には mattn さんに付き合ってもらった。この場でお礼を。