dep を使用した Go の依存関係管理


アップデート @ 2019-12-13: Starting from Go 1.13, please use the built-in Go Module to manage dependencies.


アップデート @ 2018-11-26: Technology is not just moving at a breakneck speed but also changing rapidly. Within a year, this article is OUTDATED!

And according to the dep project page:

dep was the “official experiment.” The Go toolchain, as of 1.11, has (experimentally) adopted an approach that sharply diverges from dep. As a result, we are continuing development of dep, but gearing work primarily towards the development of an alternative prototype for versioning behavior in the toolchain.

For more information about the new Go build-in management, please refer to the official GitHub Wiki - Go 1.11 Modules.

Thanks John Arundel @bitfield and Erhan Yakut @yakuter for revealing the problem. 🙇


アップデート @ 2018-02-03: godepチームの Sam Boyer 氏より、一部の情報が誤っているのとの指摘があり、修正しました。Sam Boyer 氏と読者の皆さま、申し訳ありませんでした。 😢


以前書いたGlide を使用した Go の依存関係管理の記事について、Glide チームが golang チームによって作られた dep という別の依存関係管理ツールにユーザーを誘導している、というフィードバックを得ることができました。

The Go community now has the dep project to manage dependencies. Please consider trying to migrate from Glide to dep… Glide will continue to be supported for some time but is considered to be in a state of support rather than active feature development.

Go 1.10 リリース では dep をツールチェーンに組み込むという計画があるようですが、まだ道のりは長いようです。

アップデート @ 2018-02-03:

  • dep が正式にリリースされた。
  • dep は1.10のツールチェーンには組み込まれず。最新情報はロードマップより確認可能。
まだまだスピード不足 🐌

$GOPATH の中にプロジェクトを作る

以前と同じように、新しいプロジェクトをルートパス $GOPATH/src/gitlab.com/ykyuen/dep-example に作り、以下のファイルを追加しましょう。

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import humanize "github.com/dustin/go-humanize"
import "fmt"

func main() {
  fmt.Println("hello world")
  fmt.Printf("That file is %s.\n", humanize.Bytes(82854982)) // That file is 83 MB.
  fmt.Printf("You're my %s best friend.\n", humanize.Ordinal(193)) // You are my 193rd best friend.
  fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491.
}

dep について

Gopkg.toml と Gopkg.lock

depGopkg.tomlGopkg.lock という二つのファイルを読み込みます。これらのファイルを初期化しましょう。

1
2
3
[ykyuen@camus dep-example]$ dep init
  Using master as constraint for direct dep github.com/dustin/go-humanize
  Locking in master (bb3d318) for direct dep github.com/dustin/go-humanize

ご覧のように、dep init コマンドはソースコードをスキャンし、プロジェクトに必要なパッケージをすべて vendor フォルダにダウンロードします。

Gopkg.lockglide.lock ファイルとまったく同じ機能を果たし、Gopkg.toml でバージョンを維持する必要がある場合を除いて、パッケージのバージョンをロックします。つまり、Gopkg.lock ファイルは自動生成され、Gopkg.toml によってコントロールされるバージョンのソース内の import ステートメントに依存するのです。

依存関係のバージョンをアップデートする

Gopkg.toml を編集して、最新の master ブランチのかわりに go-humanize のすこし古いバージョンを使用してみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
#   name = "github.com/user/project"
#   version = "1.0.0"
#
# [[constraint]]
#   name = "github.com/user/project2"
#   branch = "dev"
#   source = "github.com/myfork/project2"
#
# [[override]]
#  name = "github.com/x/y"
#  version = "2.4.0"


[[constraint]]
  #branch = "master"
  name = "github.com/dustin/go-humanize"
  revision = "0b19b17f90333e44518aa31bbf8126017960aee3"

次に dep ensure を実行し、パッケージを希望のバージョンに更新します。以下は、更新された_ Gopkg.lock_ の diff です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
--- /home/ykyuen/go/src/gitlab.com/ykyuen/dep-example/Gopkg.lock
+++ # This file is autogenerated, do not edit; changes
@@ -2,14 +2,13 @@
 
 
 [[projects]]
-  branch = "master"
   name = "github.com/dustin/go-humanize"
   packages = ["."]
-  revision = "bb3d318650d48840a39aa21a027c6630e198e626"
+  revision = "0b19b17f90333e44518aa31bbf8126017960aee3"
 
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "a5d65a2bdf47e41b99ad71813142ae93970f5806c77630b2aea665fe631dde23"
+  inputs-digest = "0023bfe634a061b89ae0fbd71e3236f3f75f0843a0c974eeb9822040c0ea2dc4"
   solver-name = "gps-cdcl"
   solver-version = 1
 

新しい依存関係を追加する

dep ensure -add コマンドを使用して、新しいパッケージを追加します。

1
2
3
4
5
[ykyuen@camus dep-example]$ dep ensure -add github.com/leekchan/accounting
Fetching sources...

"github.com/leekchan/accounting" is not imported by your project, and has been temporarily added to Gopkg.lock and vendor/.
If you run "dep ensure" again before actually importing it, it will disappear from Gopkg.lock and vendor/.

これで新しい accounting パッケージが vendor フォルダに用意され、新しい制約が Gopkg.toml に書き込まれ、Gopkg.lock にロックされました。以下のように main.go を更新してみましょう。

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import humanize "github.com/dustin/go-humanize"
import accounting "github.com/leekchan/accounting"
import "math/big"
import "fmt"

func main() {
  fmt.Println("hello world")
  fmt.Printf("That file is %s.\n", humanize.Bytes(82854982)) // That file is 83 MB.
  fmt.Printf("You're my %s best friend.\n", humanize.Ordinal(193)) // You are my 193rd best friend.
  fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491.

  ac := accounting.Accounting{Symbol: "$", Precision: 2}
  fmt.Println(ac.FormatMoney(123456789.213123))                       // "$123,456,789.21"
  fmt.Println(ac.FormatMoney(12345678))                               // "$12,345,678.00"
  fmt.Println(ac.FormatMoney(big.NewRat(77777777, 3)))                // "$25,925,925.67"
  fmt.Println(ac.FormatMoney(big.NewRat(-77777777, 3)))               // "-$25,925,925.67"
  fmt.Println(ac.FormatMoneyBigFloat(big.NewFloat(123456789.213123))) // "$123,456,789.21"
}

そして実行します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[ykyuen@camus dep-example]$ go run main.go
hello world
That file is 83 MB.
You're my 193rd best friend.
You owe $6,582,491.
$123,456,789.21
$12,345,678.00
$25,925,925.67
-$25,925,925.67
$123,456,789.21

git サブモジュールの問題

Glidedep の大きな違いは、パッケージのサブモジュールが無視されるという点です。たとえば、go-goracle/goracle パッケージを dep で追加したあと、odpi サブモジュールはからになり、エラーになります。サブモジュールが無視される理由については以下をご参照ください。

アップデート @ 2018-02-03:

gitサブモジュールに関する記述に誤りがありました。

Sam Boyer 氏より:

dep should be perfectly fine at pulling in git submodules in the case you describe. I just replicated what you describe here locally, and the problem isn’t submodules — it’s that there’s no Go code in github.com/go-goracle/goracle/odpi, so it can’t be imported directly.

You likely need to turn off unused-packages pruning in Gopkg.toml for that project specifically, as otherwise dep ensure will automatically remove what appears to be an unused directly (but it seems it’s actually used by cgo).

アップデート @ 2018-03-04:

go-goracle/goracle パッケージは dep で動作しないことが判明しました。dep チームが発信するこの問題についての最新情報は以下から確認できます。

まとめ

  • depGolang コミュニティの公式の依存関係管理ツールである可能性が高い。
  • 新しい Golang プロジェクトを始めるのであれば、dep がおすすめ。
  • 既存のプロジェクトで Glide を使用している場合、dep に移行することを検討することも可能だが、dep の正式リリースまで Glide をしばらく使用しても問題ないと思われる。
  • さらに、パッケージのサブモジュールがない場合エラーが発生する可能性がある。
  • dep が正式にリリースされた。
  • dep は git サブモジュールのパッケージと互換性がある。
  • 可能な限り標準ライブラリを使用したほうがよい。
  • gitlab.comで実例を参照することができる。