Go1.11から試験導入されるバージョン管理機能(vgo)について

2018.4.20 追記: golang.tokyo #14にて本記事の内容で登壇しました。

vgoとは

Go1.11から、goコマンドにバージョン管理機能が試験的に導入される予定です。 Go1.12から正式サポートを目指しており、現在はプロトタイプ版としてvgoコマンドを使用することができます。 vgoは従来のdepとは異なるアプローチでバージョン管理を行います。 本記事は、vgoによって変更が発生するポイントについてまとめたものです。

vgoの詳細はこちらから確認できます。
https://research.swtch.com/vgo

依存関係の解決はbuild時に行う

ビルドはvgo buildで行います。 vgo buildの実行時に未知のimportを検出すると、その最新バージョンを取得します。 個別にvgo getする必要はありません(することもできます)。

$ vgo build
vgo: resolving import "rsc.io/quote"
vgo: finding rsc.io/quote (latest)
vgo: adding rsc.io/quote v1.5.2
vgo: finding rsc.io/quote v1.5.2
vgo: finding rsc.io/sampler v1.3.0
vgo: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
vgo: downloading rsc.io/quote v1.5.2
vgo: downloading rsc.io/sampler v1.3.0
vgo: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c

また、パッケージの依存関係はgo.modというファイルで管理します。 vgo buildもしくはvgo getによって、go.modの内容は自動的に書き換えられます。 下記はgo.modファイルの例です。

module "github.com/you/hello"

require (
    "golang.org/x/text" v0.0.0-20180208041248-4e4a3210bb54
    "rsc.io/quote" v1.5.2
    "rsc.io/sampler" v1.99.99
)

vendorディレクトリが不要になる

従来のパッケージ管理とは異なり、vendorディレクトリは使用しません。 vgoによって取得した依存パッケージは$GOPATH/src/v以下にてバージョン別に保管され、go.modファイルによって参照されます。

$GOPATH/src/v/
└── rsc.io
    ├── [email protected]
    ├── [email protected]
    └── [email protected]

vendorディレクトリによる管理が必要な場合は、vgo vendorコマンドによってvendorディレクトリを作成することもできます。 $GOPATH/src/vを参照する必要が無くなるため、通常のベンダリングとしてgo buildすることができます。

$ vgo vendor
$ tree vendor
vendor/
├── github.com
│   └── tanksuzuki
│       ├── vgo-a
│       │   ├── README.md
│       │   ├── a.go
│       │   └── go.mod
│       ├── vgo-b
│       │   ├── README.md
│       │   ├── b.go
│       │   └── go.mod
│       └── vgo-c
│           ├── README.md
│           ├── c.go
│           └── go.mod
└── vgo.list
$ cat vendor/vgo.list
MODULE                          VERSION
github.com/tanksuzuki/vgo-test  -
github.com/tanksuzuki/vgo-a     v1.1.0
github.com/tanksuzuki/vgo-b     v1.1.0
github.com/tanksuzuki/vgo-c     v1.1.0

破壊的な変更を加える場合はimport pathを変更する

vgoによって管理するパッケージは、 後方互換性を持つことが義務化 されます。

If an old package and a new package have the same import path, the new package must be backwards-compatible with the old package.
https://research.swtch.com/vgo-intro

ただし、どうしても破壊的な変更を加えなければならないケースもあります。 その場合はimport pathを変更し、別のパッケージとして扱うことで対処します。 例えばgithub.com/tanksuzuki/pkgという名前のパッケージに破壊的な変更を加える場合、github.com/tanksuzuki/pkg/v2のようにパスを変更します。 また、gopkg.inのようなAPIでパスを変えることでも対処できます。

セマンティックバージョニング

パッケージの作者はセマンティックバージョンニングにより、各リリースにタグ付け(git tag)することが期待されます。 vgoはバージョン管理にタグを使用します。 下記の例では、rsc.io/quotev1.5.2を使用していることが確認できます。

module "github.com/you/hello"

require (
    "golang.org/x/text" v0.0.0-20180208041248-4e4a3210bb54
    "rsc.io/quote" v1.5.2
    "rsc.io/sampler" v1.99.99
)

また、タグ付けされていないコミットを参照する場合、擬似的なバージョン(v0.0.0-yyyymmddhhmmss-commit)が与えられます。 上記の例では、golang.org/x/textがそれに当たります。

可能な限り古いバージョンを選択する

dep等のパッケージマネージャーは、依存関係解決の際に「可能な限り最新のパッケージ」を選択します。 それに対し、vgoは「指定した範囲で、可能な限り最も古いパッケージ」を使用します。 直感に反する挙動ですが、より古いバージョンが公開されることは無いためビルドの再現性を保ち続けます。

vgoでは依存しているパッケージバージョンの下限のみを制限できるようになっています。 下限を下回らない範囲で極小のバージョンが選ばれることで、システム全体としても小さいサイズを保つことができます。

vgoの普及によってdepは廃止される

vgoが普及するまで、depのサポートは継続するとのこと。

We will keep dep available until the path to full go command integration is decided, implemented, and generally available.
https://research.swtch.com/vgo-intro

将来的に(バージョン管理機能が無い)go getは廃止される

vgoが正式に取り込まれた後ですが、単純に最新パッケージを$GOPATHにダウンロードするだけのgo getは廃止されます。

I would like Go 1.11 to ship with preliminary support for Go modules, as a kind of technology preview, and then I’d like Go 1.12 to ship with official support. In some later release, we’ll remove support for the old, unversioned go get.
https://research.swtch.com/vgo-intro

まとめ

  • 破壊的な変更を加える場合、import pathを変えることが義務付けられます。
  • セマンティックバージョニングでタグ付けすることが期待されます。
  • depはすぐに廃止されません。また、移行方法についても考慮されています。