Glide から Dep への移行を検討する

(この記事は Qiita とのマルチポストです。 まぁ,向こうは草稿版だけど)

久しぶりに glide を使おうと最新版(0.13.0)を見に行ったら “Consider switching to dep” とか書いてあるじゃない。

Glide is used by a great number of projects and will continue to get support for some time. But, the near future is likely in dep. dep can handle importing Glide config files. Please consider trying dep on your project or converting to dep.

まじすか。

つまり「依存関係(Vendoring)管理ツールとしては dep を推奨するけど移行できない人のために当面はサポートを続けるよ(でも将来は分からん)」という解釈でいいのだろうか。

depGo 言語開発プロジェクトの公式ツールで,2017年の始めくらいに日本でも話題になったような気がするが,私は glide で完全に満足していたのでスルーしていた。 こんなことになるなんて。 ならもう dep に移行するしかないぢゃん。

とはいえ,いきなり本番環境に投入するのは怖いので,なにか適当なテストケースはないか,と自分のリポジトリを漁ってたら丁度いいのがあったよ。

これってモンテカルロ法で遊んでたときに作ったものだ。 最悪ぶっ壊れてもいいので,これ使って試してみるか。

dep の取得

まず dep の取得から始めないとだが,リポジトリ自体は go get コマンドで取得できる。

$ go get -u github.com/golang/dep/cmd/dep

これをこのまま使ってもいいのだが,リリースページにビルド済みのモジュールが置かれているので,ありがたくこれを使わせてもらおう。

最新版(現時点で v0.4.1)には Windows 用のモジュール dep-windows-amd64.exe もある。 これを dep.exe にリネームして使う。

万が一があっては困るのでモジュールの SHA256 ハッシュ値を確認しておく(こういうのこそ OpenPGP を使ってくれないものか)。 Windows ユーザで Windows 8.1 以降であれば PowerShell(4.0 以上)で Get-FileHash コマンドレットが使える1

PS C:\Users\username\Downloads> Get-FileHash dep-windows-amd64.exe -Algorithm SHA256 | Format-List

Algorithm : SHA256
Hash      : F6E6A872C54D5AE7536AC71FD5BCAC9F4E7B8A1DAFA1EF7C23866E2F3069FE4E
Path      : C:\Users\username\Downloads\dep-windows-amd64.exe

これを dep-windows-amd64.exe.sha256 に記載されている値と比較する。 改竄されてなければ同じ値になるはずである。 目視は辛いのでテキストエディタ等の検索機能を使えばいいだろう。

【2017-10-31 追記】 だんだん面倒になってきたのでハッシュ値を計算するツールを作った。 詳しくは「Hash 値を計算するパッケージを作ってみた」を参照のこと。

実行モジュールの動作確認もしておく。

$ dep
Dep is a tool for managing dependencies for Go projects

Usage: "dep [command]"

Commands:

  init     Set up a new Go project, or migrate an existing one
  status   Report the status of the project's dependencies
  ensure   Ensure a dependency is safely vendored in the project
  prune    Pruning is now performed automatically by dep ensure.
  version  Show the dep version information

Examples:
  dep init                               set up a new project
  dep ensure                             install the project's dependencies
  dep ensure -update                     update the locked versions of all dependencies
  dep ensure -add github.com/pkg/errors  add a dependency to the project

Use "dep help [command]" for more information about a command.

$ dep version
dep:
 version     : v0.4.1
 build date  : 2018-01-24
 git hash    : 37d9ea0a
 go version  : go1.9.1
 go compiler : gc
 platform    : windows/amd64

glide から dep への移行

お試し用の spiegel-im-spiegel/pi をビルド可能な適当な場所に置く。

このパッケージの glide.yaml はこんな感じになっている。

package: github.com/spiegel-im-spiegel/pi
import:
- package: github.com/spiegel-im-spiegel/gocli
- package: github.com/spf13/cobra
- package: github.com/pkg/errors
- package: github.com/seehuhn/mt19937
- package: github.com/davidminor/gorand
- package: github.com/davidminor/uint128

また glide.lock はこんな感じ。

hash: d570123d6231810c51dd17e415673df221fb2dec7ef6ab45cd34093002a87cbb
updated: 2016-11-16T17:28:38.2997832+09:00
imports:
- name: github.com/davidminor/gorand
  version: 189780b8053a44a111339a4248394fd844c1da40
  subpackages:
  - lcg
- name: github.com/davidminor/uint128
  version: 5745f1bf80414e0ad2670e85d6aece8c58031def
- name: github.com/inconshreveable/mousetrap
  version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
- name: github.com/pkg/errors
  version: 248dadf4e9068a0b3e79f02ed0a610d935de5302
- name: github.com/seehuhn/mt19937
  version: 98c0ea580d2f3c5a171acf4d4f15321b72209d08
- name: github.com/spf13/cobra
  version: 6b74a60562f5c1c920299b8f02d153e16f4897fc
- name: github.com/spf13/pflag
  version: 5ccb023bc27df288a957c5e994cd44fd19619465
- name: github.com/spiegel-im-spiegel/gocli
  version: 5929f04fb8e4a19ac29fdf658866f9441f339cd9
testImports: []

この状態で dep init コマンドを実行する。

$ dep init
Importing configuration from glide. These are only initial constraints, and are further refined during the solve process.                                                                             
Detected glide configuration files...                                                              
Converting from glide.yaml and glide.lock...                                                       
  Trying v0.3.0 (5929f04) as initial lock for imported dep github.com/spiegel-im-spiegel/gocli     
  Trying * (6b74a60) as initial lock for imported dep github.com/spf13/cobra                       
  Trying * (248dadf) as initial lock for imported dep github.com/pkg/errors                        
  Trying master (98c0ea5) as initial lock for imported dep github.com/seehuhn/mt19937              
  Trying * (189780b) as initial lock for imported dep github.com/davidminor/gorand                 
  Trying master (5745f1b) as initial lock for imported dep github.com/davidminor/uint128           
  Trying v1.0 (76626ae) as initial lock for imported dep github.com/inconshreveable/mousetrap      
  Trying * (5ccb023) as initial lock for imported dep github.com/spf13/pflag                       
  Locking in  (248dadf) for direct dep github.com/pkg/errors                                       
  Locking in  (6b74a60) for direct dep github.com/spf13/cobra                                      
  Using master as constraint for direct dep github.com/seehuhn/mt19937                             
  Locking in master (98c0ea5) for direct dep github.com/seehuhn/mt19937                            
  Using ^0.3.0 as constraint for direct dep github.com/spiegel-im-spiegel/gocli                    
  Locking in v0.3.0 (5929f04) for direct dep github.com/spiegel-im-spiegel/gocli                   
  Locking in  (189780b) for direct dep github.com/davidminor/gorand                                

実は spiegel-im-spiegel/gocli パッケージの最新版は v0.5.0 だが, glide.lock の内容を読み取って,ちゃんと v0.3.0 のものを取ってきているようだ。 偉いぞ!

dep init コマンドにより Gopkg.toml および Gopkg.lock の2つのファイルと vendor/ フォルダが作成される。 このうち Gopkg.toml の内容は以下の通り。

[[constraint]]
  branch = "master"
  name = "github.com/seehuhn/mt19937"

[[constraint]]
  name = "github.com/spiegel-im-spiegel/gocli"
  version = "0.3.0"

[prune]
  go-tests = true
  unused-packages = true

そして Gopkg.lock の内容は以下の通り。

[[projects]]
  name = "github.com/davidminor/gorand"
  packages = ["lcg"]
  revision = "189780b8053a44a111339a4248394fd844c1da40"

[[projects]]
  branch = "master"
  name = "github.com/davidminor/uint128"
  packages = ["."]
  revision = "5745f1bf80414e0ad2670e85d6aece8c58031def"

[[projects]]
  name = "github.com/inconshreveable/mousetrap"
  packages = ["."]
  revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
  version = "v1.0"

[[projects]]
  name = "github.com/pkg/errors"
  packages = ["."]
  revision = "248dadf4e9068a0b3e79f02ed0a610d935de5302"

[[projects]]
  branch = "master"
  name = "github.com/seehuhn/mt19937"
  packages = ["."]
  revision = "98c0ea580d2f3c5a171acf4d4f15321b72209d08"

[[projects]]
  name = "github.com/spf13/cobra"
  packages = ["."]
  revision = "6b74a60562f5c1c920299b8f02d153e16f4897fc"

[[projects]]
  name = "github.com/spf13/pflag"
  packages = ["."]
  revision = "5ccb023bc27df288a957c5e994cd44fd19619465"

[[projects]]
  name = "github.com/spiegel-im-spiegel/gocli"
  packages = ["."]
  revision = "5929f04fb8e4a19ac29fdf658866f9441f339cd9"
  version = "v0.3.0"

[solve-meta]
  analyzer-name = "dep"
  analyzer-version = 1
  inputs-digest = "3f9a0c0024e81ba251efaa0cb0014694f8315add84c7e8044a346f370e3e088e"
  solver-name = "gps-cdcl"
  solver-version = 1

glide.lockGopkg.lock の内容がマッチしているのが分かると思う。

【2018-02-02 追記】 dep v0.4 から挙動が変わった? どうやら dep init 時点でリビジョン管理が必要と判断されるパッケージのみ Gopkg.toml に記載される感じ。 それ以外でリビジョン管理が必要なものは手動で Gopkg.toml に記述する必要があるかも。

念のため dep status も見ておこう。

$ dep status
PROJECT                               CONSTRAINT     VERSION        REVISION  LATEST   PKGS USED
github.com/davidminor/gorand          *                             189780b            1
github.com/davidminor/uint128         branch master  branch master  5745f1b   5745f1b  1
github.com/inconshreveable/mousetrap  v1.0           v1.0           76626ae   v1.0     1
github.com/pkg/errors                 *                             248dadf            1
github.com/seehuhn/mt19937            branch master  branch master  98c0ea5   98c0ea5  1
github.com/spf13/cobra                *                             6b74a60            1
github.com/spf13/pflag                *                             5ccb023            1
github.com/spiegel-im-spiegel/gocli   ^0.3.0         v0.3.0         5929f04   v0.3.0   1

ビルドもちゃんと通る。

$ go build -v .
github.com/spiegel-im-spiegel/pi/vendor/github.com/davidminor/uint128
github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/pflag
github.com/spiegel-im-spiegel/pi/vendor/github.com/seehuhn/mt19937
github.com/spiegel-im-spiegel/pi/vendor/github.com/inconshreveable/mousetrap
github.com/spiegel-im-spiegel/pi/vendor/github.com/spiegel-im-spiegel/gocli
github.com/spiegel-im-spiegel/pi/vendor/github.com/davidminor/gorand/lcg
github.com/spiegel-im-spiegel/pi/vendor/github.com/pkg/errors
github.com/spiegel-im-spiegel/pi/gencmplx
github.com/spiegel-im-spiegel/pi/qq
github.com/spiegel-im-spiegel/pi/genpi
github.com/spiegel-im-spiegel/pi/plot
github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/cobra
github.com/spiegel-im-spiegel/pi/estmt
github.com/spiegel-im-spiegel/pi/cmd
github.com/spiegel-im-spiegel/pi

というわけで, glide.yamlglide.lock が正しい状態で残っていれば問題なく dep に移行できそうだ。

依存関係の管理

Gopkg.toml を以下のように修正してみる。

[[constraint]]
  name = "github.com/davidminor/gorand"
  branch = "master"

[[constraint]]
  name = "github.com/pkg/errors"
  version = "0.8.*"

[[constraint]]
  name = "github.com/seehuhn/mt19937"
  branch = "master"

[[constraint]]
  name = "github.com/spf13/cobra"
  version = "0.0.*"

[[constraint]]
  name = "github.com/spiegel-im-spiegel/gocli"
  version = "0.3.*"

[prune]
  go-tests = true
  unused-packages = true

この状態で dep ensure コマンドを実行しステータスを見ると,以下のような感じになる。

$ dep status
PROJECT                               CONSTRAINT     VERSION        REVISION  LATEST   PKGS USED
github.com/davidminor/gorand          branch master  branch master  283446f   283446f  1
github.com/davidminor/uint128         branch master  branch master  5745f1b   5745f1b  1
github.com/inconshreveable/mousetrap  v1.0           v1.0           76626ae   v1.0     1
github.com/pkg/errors                 ^0.8.0         v0.8.0         645ef00   v0.8.0   1
github.com/seehuhn/mt19937            branch master  branch master  98c0ea5   98c0ea5  1
github.com/spf13/cobra                ^0.0.1         v0.0.1         7b2c5ac   v0.0.1   1
github.com/spf13/pflag                *                             5ccb023            1
github.com/spiegel-im-spiegel/gocli   ^0.3.0         v0.3.0         5929f04   v0.3.0   1

たとえば

[[constraint]]
  name = "github.com/davidminor/gorand"
  branch = "master"

であれば github.com/davidminor/gorand パッケージで master ブランチの最新コミットを取ってくる。 また

[[constraint]]
  name = "github.com/spiegel-im-spiegel/gocli"
  version = "0.3.*"

であれば spiegel-im-spiegel/gocli パッケージで v0.3.x の最新バージョンを取ってくる2

[prune] 指定では vendor/ フォルダから除外するパッケージやファイルを指定する。

[prune]
  go-tests = true
  unused-packages = true

go-tests はテスト用のファイル(*_test.go)を unused-packages は未使用のパッケージを指す。 なお,値は true 以外はエラーになるようだ。 たとえば未使用パッケージも含めたいのであれば unused-packages = false とするのではなく記述自体を削除する。

[prune]
  go-tests = true

依存関係の視覚化

dep status コマンドには結果を DOT 言語で吐き出すオプションがあるようだ。 Graphviz があれば,この出力結果を画像データに変換できる。

$ dep status -dot | dot -Tpng -o pi-dependency.png

結果はこんな感じ。

pi-dependency.png

ブラボー!

リポジトリへのパスを直接指定する

GitHub みたいな有名 SaaS に置いてあるパッケージなら Gopkg.toml

[[constraint]]
  name = "github.com/spiegel-im-spiegel/gocli"
  version = "0.3.*"

とか書けば適切に処理してくれるけど,有名でない SaaS ディレクトリや職場 LAN のリポジトリ上のパッケージではこうはいかないこともある。 こういう場合には,以下に示す通り,直接リポジトリへの(プロトコルを含めた)パスを指定する。

[[constraint]]
  name = "github.com/spiegel-im-spiegel/gocli"
  source = "git@github.com:spiegel-im-spiegel/gocli.git"
  version = "0.3.* "

これで dep ensure すれば

$ dep ensure -v

...

(1/8) Wrote github.com/spiegel-im-spiegel/gocli (from git@github.com:spiegel-im-spiegel/gocli.git)@v0.3.0
(2/8) Wrote github.com/pkg/errors@248dadf4e9068a0b3e79f02ed0a610d935de5302
(3/8) Wrote github.com/davidminor/gorand@189780b8053a44a111339a4248394fd844c1da40
(4/8) Wrote github.com/spf13/pflag@5ccb023bc27df288a957c5e994cd44fd19619465
(5/8) Wrote github.com/inconshreveable/mousetrap@v1.0
(6/8) Wrote github.com/davidminor/uint128@master
(7/8) Wrote github.com/spf13/cobra@6b74a60562f5c1c920299b8f02d153e16f4897fc
(8/8) Wrote github.com/seehuhn/mt19937@master

といった感じになる。 ちゃんと指定したリポジトリからパッケージを取得してきているのが分かるだろう。

Go 1.9 から glide novendor は必要なくなった

Vendoring で一番あつかいに困るのがテストで,たとえば安直に

$ go test -v ./...

とかやると vendor/ フォルダ以下のパッケージまでテスト・シーケンスが走ってしまうのが困りものであった。 このため glide にはこれを回避する glide novendor コマンドがあって

$ go test -v $(glide novendor)

とすることで vendor/ フォルダへのテストを回避できるようになっていたのだ3

ところがところが!!

Go 言語 1.9 からは ./... の扱いが変更になり

“By popular request, ./... no longer matches packages in vendor directories in tools accepting package names, such as go test.”

ということで ./...vendor/ フォルダ以下が含まれないことになったのだ。 たとえば spiegel-im-spiegel/pi パッケージの場合は

$ go list ./...
github.com/spiegel-im-spiegel/pi
github.com/spiegel-im-spiegel/pi/cmd
github.com/spiegel-im-spiegel/pi/estmt
github.com/spiegel-im-spiegel/pi/gencmplx
github.com/spiegel-im-spiegel/pi/genpi
github.com/spiegel-im-spiegel/pi/plot
github.com/spiegel-im-spiegel/pi/qq

となる。 逆に vendor/ フォルダも含めたいなら

$ go list ./... ./vendor/...
github.com/spiegel-im-spiegel/pi
github.com/spiegel-im-spiegel/pi/cmd
github.com/spiegel-im-spiegel/pi/estmt
github.com/spiegel-im-spiegel/pi/gencmplx
github.com/spiegel-im-spiegel/pi/genpi
github.com/spiegel-im-spiegel/pi/plot
github.com/spiegel-im-spiegel/pi/qq
github.com/spiegel-im-spiegel/pi/vendor/github.com/davidminor/gorand/lcg
github.com/spiegel-im-spiegel/pi/vendor/github.com/davidminor/gorand/pcg
github.com/spiegel-im-spiegel/pi/vendor/github.com/davidminor/uint128
github.com/spiegel-im-spiegel/pi/vendor/github.com/inconshreveable/mousetrap
github.com/spiegel-im-spiegel/pi/vendor/github.com/pkg/errors
github.com/spiegel-im-spiegel/pi/vendor/github.com/seehuhn/mt19937
github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/cobra
github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/cobra/cobra
github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/cobra/cobra/cmd
github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/cobra/doc
github.com/spiegel-im-spiegel/pi/vendor/github.com/spf13/pflag
github.com/spiegel-im-spiegel/pi/vendor/github.com/spiegel-im-spiegel/gocli

とすればよい。 こっちのほうが遥かに扱いやすいよね。

これでまたひとつ glide が「要らない子」になる理由が増えてしまったのだった。

ブックマーク


  1. Windows 7 の場合は “Windows Management Framework 4.0” をインストールすることで PowerShell 4.0 にアップグレードできる。 ↩︎

  2. Gopkg.toml のバージョンの考え方は “Semantic Versioning” に従っている。ワイルドカード等を使ったバージョン指定については Masterminds/semver パッケージを参照するとよい。 ↩︎

  3. glide を使わない場合は go test -v $(go list ./... | grep -v /vendor/) とかする。どのみち Windows のコマンドプロンプトでは無理だけど(笑) ↩︎