背景
依存するモジュールのメジャーバージョンがv2以上の場合に、以下のようにバージョン指定すると
$ go get github.com/xxx/yyy@v2.0.1
次のように怒られます。
require github.com/xxx/yyy: version "v2.0.1" invalid: should be v0 or v1, not v2
今回はこの対応方法について説明します。
環境
- go 1.16.3
version "vX.X.X" invalid: should be v0 or v1, not v2
原因
go modulesは複数の依存モジュールA
, B
が同じ依存モジュールC
に依存している場合、A
とB
のgo.modでより新しいバージョンのC
を使おうとします。
メジャーバージョンアップのように後方互換性を持たない場合はどうなるでしょう? そうなると古いメジャーバージョンに依存していたモジュールは期待する動作ができなくなります。
これの対応方法として、go modulesはimport pathにメジャーバージョンを記入する方針を取っています。
互換性のルールとして
"If an old package and a new package have the same import path, the new package must be backwards compatible with the old package."
ref: research!rsc: Semantic Import Versioning (Go & Versioning, Part 3)
とあるように、同じimport pathなら互換性を保つ。互換性を保たないなら別のimport pathに、ということです。
しかし現状これがあまり周知されておらず、メジャーバージョンがv2以上にも関わらずimport pathがそのままのモジュールが多数あります。
今回のケースもそれに当たります。
対応方法
これを解決する方法としては以下の3通りがあります。
- モジュール側を正しいgo.modに直し、使用側のimport pathを修正する(正攻法)
+incompatible
をつける- 依存モジュールのgo.modを消す
a. モジュール側を正しいgo.modに直し、使用側のimport pathを修正する(正攻法)
モジュール側
go modulesの仕様の通り、モジュール側のgo.modにバージョンをつけます。
v2であれば
module github.com/xxx/yyy go 1.16
を
module github.com/xxx/yyy/v2 go 1.16
にします。
使用側
また使用する側もimport pathにv2
をつけます。
package main import "github.com/xxx/yyy"
だったのを
package main import "github.com/xxx/yyy/v2"
にします。
b. +incompatible
をつける
モジュール側が自分たちの管理でなく、イジれない場合は使用側のみで解決する必要があります。
go.modを以下のようにし
require github.com/xxx/yyy v2.2.0+incompatible
Checksumデータベースの検証時でもコケないよう、GOPRIVATE
をつけてgo mod tidy
します。
GOPRIVATE=github.com/xxx/yyy go mod tidy
c. 依存モジュールのgo.modを消す
go.modが無い状態であれば、メジャーバージョン指定がv2
以上でも勝手に+incompatible
が付きます。
なのでgo.mod未対応なリポジトリであれば気にせず利用できます。GOPRIVATE
も不要です。
具体的なコードで検証
依存モジュールが以下のケースの時にどういった状態になるかを一通り確認します。
- リリースタグがない
- リリースタグがあるがマイナーバージョンしかない
- リリースタグがあり、v1.0.0以上v2.0.0未満
- リリースタグがあり、v2.0.0以上(go.modなし)
- リリースタグがあり、v2.0.0以上(path変更なし)
- リリースタグがあり、v2.0.0以上(正攻法)
↓を依存モジュールとして利用します。
リリースタグがない
モジュール側
masterにコミットをマージしておきます。
使用側
リリースタグがないのでmasterブランチやコミットハッシュを使って指定します。
$ go get github.com/jun06t/go-modules-test@master
以下のようなgo.modファイルになります。
module github.com/jun06t/go-sample/modules go 1.16 require github.com/jun06t/go-modules-test v0.0.0-20210430152744-b043f1aff7e1
リリースタグがあるがマイナーバージョンしかない
モジュール側
使用側
リリースタグがあるのでそれを指定します。
$ go get github.com/jun06t/go-modules-test@v0.1.0
以下のようなgo.modファイルになります。
module github.com/jun06t/go-sample/modules go 1.16 require github.com/jun06t/go-modules-test v0.1.0
リリースタグがあり、v1.0.0以上v2.0.0未満
モジュール側
使用側
リリースタグがあるのでそれを指定します。
$ go get github.com/jun06t/go-modules-test@v1.2.3
以下のようなgo.modファイルになります。
module github.com/jun06t/go-sample/modules go 1.16 require github.com/jun06t/go-modules-test v1.2.3
リリースタグがあり、v2.0.0以上(go.modなし)
モジュール側
使用側
$ go get github.com/jun06t/go-modules-test@v2.0.1
で勝手に+incompatible
が付きます。
module github.com/jun06t/go-sample/modules go 1.16 require github.com/jun06t/go-modules-test v2.0.1+incompatible
リリースタグがあり、v2.0.0以上(path変更なし)
モジュール側
使用側
$ go get github.com/jun06t/go-modules-test@v2.0.2
では
invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2
とエラーが出てできないので、go.modを以下のように直接いじります。
module github.com/jun06t/go-sample/modules go 1.16 require github.com/jun06t/go-modules-test v2.0.2+incompatible
次にgo mod tidy
しますが、そのまま実行するとChecksumデータベースによる検証時に
verifying go.mod: github.com/jun06t/go-modules-test@v2.0.2+incompatible/go.mod: reading https://sum.golang.org/lookup/github.com/jun06t/go-modules-test@v2.0.2+incompatible: 410 Gone server response: not found: github.com/jun06t/go-modules-test@v2.0.2+incompatible: invalid version: +incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required
とエラーが出るので、Checksum検証の対象外にするためにGOPRIVATE
をつけます。
$ GOPRIVATE=github.com/jun06t go mod tidy go: downloading github.com/jun06t/go-modules-test v2.0.2+incompatible
これで成功です。
リリースタグがあり、v2.0.0以上(正攻法)
モジュール側
go.modに以下のpathを追加します。
※モジュール側でパッケージ内依存があれば、各パッケージでもimport pathに/v2
を付ける必要があります
module github.com/jun06t/go-modules-test/v2 go 1.16
使用側
import pathに/v2
をつけます。
package main import ( echo "github.com/jun06t/go-modules-test/v2" ) func main() { echo.Call() }
go get時もv2
をつけます。
$ go get github.com/jun06t/go-modules-test/v2 go: downloading github.com/jun06t/go-modules-test/v2 v2.1.0 go get: added github.com/jun06t/go-modules-test/v2 v2.1.0
以下のようなgo.modファイルになります。
module github.com/jun06t/go-sample/modules go 1.16 require github.com/jun06t/go-modules-test/v2 v2.1.0
まとめ
v2以上のtagをつけているリポジトリではgo.modやimport pathもそのメジャーバージョンに合わせるようにしましょう。
自分が↑の修正をできないリポジトリであれば、+incompatible
で回避するようにしましょう。