Goのプロジェクトの依存パッケージをどうするかという問題があります。
package myapp import ( "gopkg.in/hoge/fuga" "myserver.com/foo/bar" "buz" ) func main() { // ... }
などのアプリケーションをサーバにデプロイするとき、fuga
パッケージやbar
、buz
パッケージをどこから調達するかです。
たとえばNodeJSならpackage.json
とnpm install
が、RubyならGemfile
とbundle install
がこのような依存パッケージを調達してくれます。
godep
Goにおける依存パッケージの解決方法のひとつに、godep
というものがあります。
Goのパッケージマネージャの中ではいちばんメジャーなもののようです。(mattnさんのgomやrosylillyさんのgondlerもわたし気になります)
使い方
1. 依存関係を保存する
デプロイしたいアプリケーションのルートディレクトリでgodep save
を実行します*1。
This will save a list of dependencies to the file Godeps/Godeps.json, and copy their source code into Godeps/_workspace.
これは、アプリケーションが参照するすべてのパッケージのソースコードを、ローカルマシンの$GOPATH/src
から探してきて、アプリケーションのルート直下のGodeps/_workspace
にコピーします*2。これと同時に、その依存パッケージのバージョンを含めたリスト情報をGodeps/Godeps.json
に記録します。
2. 依存関係をリポジトリからインストールする
依存パッケージをgo get
していないマシンにおいて、godep resotore
を実行します。
The godep restore command is the opposite of godep save. It will install the package versions specified in Godeps/Godeps.json to your GOPATH.
Godeps.jsonの記述にしたがって、リモートのリポジトリから依存パッケージをインストール(go get)しようとします。しかしながらこれは、依存パッケージのリポジトリがgopkg.inやgithub.comなどのようにパブリックに(あるいはインターナルに到達可能に)ホスティングされている場合にのみ効果を発揮します。
3. そうではなく、saveしたソースコードから実行する
Goのバイナリはgo build``go install
、あるいはgo run
と同時に、生成され、それが実行されますが、普通は依存パッケージをローカルの$GOAPTHから解決しようとします. これらのコマンドの前にgodep go run
などとgodep
をつけることで、これをGodeps/_workspace
から解決しようとします。
ためしに、(godep saveしてある前提で)
% cd myapp % go run main.go This is main. % rm -rf $GOPATH/src/buz #依存パッケージを削除しちゃう % go run main.go main.go:3:8: cannot find package "buz" in any of: /Users/otiai10/.go/src/buz (from $GOROOT) /Users/otiai10/proj/go/src/buz (from $GOPATH) # 叱られるが % godep go run main.go This is buz.Buz (method of imported package)
このように、godep
プレフィックスをつけてgo * コマンドを実行すると、依存パッケージをGodeps/_workspace
から解決してビルドをつくる。
デプロイにあたって
したがって、サーバへデプロイする場合に、ローカルでアプリケーションの依存をgodep saveでアプリケーションのGodepsディレクトリに保存してしまい、デプロイスクリプトでこのアプリケーションを起動するときにgodep go run main.go
(あるいはgodep go install myapp; myapp
)とすればアプリケーションの依存は解決される(というか、一緒にデプロイしてしまうかんじ)ことになります。
パッケージの更新も保存しておく
とのことから、最も単純な開発フローにおいては依存パッケージ側で変更があればアプリケーションでそれをsaveする(godep update foo
)をする必要があります。
ためしに、buz
パッケージの一部分を編集して以下のようにしてコミットしてみました。
func Buz() { - fmt.Println("This is buz.Buz (method of imported package)") + fmt.Println("This is buz.Buz version 2!!") }
そしてアプリケーションでgodep update buz
をするとGodeps/_workspace
のソースコードも更新されます。そしてgodep go run main.go
とすると、
This is buz.Buz version 2!!
となるはずです。
僕がハマった罠
さらに、buz
パッケージをv3に... する前に、godep go install ./
をしてみます。
すると実は、Godep/_workspace/pkg
以下にbuz
パッケージのv2のビルドが生成されます。実はこれがくせ者でした。
あとは普通にbuz
パッケージをv3にします。
func Buz() { - fmt.Println("This is buz.Buz version 2!!") + fmt.Println("v3だよ! This is buz.Buz") }
で、godep update buz
すると、Godeps/_workspace/src
以下のbuz
パッケージのソースもv3になったので、意気揚々とgodep go run main.go
すると、、
This is buz.Buz version 2!!
!?
丁寧に書くのも飽きたので結論
godep save
やgodep update
は、godepが依存パッケージのソースコードをGodeps/_workspace/src
に保存するgodep go install
すると、godepが依存パッケージのビルドをGodeps/_workspace/pkg
に保存する- 以降、
godep go run
やgodep go install
などのビルドを伴う処理においてGodeps/_workspace/pkg
以下にビルド済みのものがあればそちらを使って依存を解決する godep update
ではGodeps/_workspace/pkg
以下のビルドは更新されないので、src
以下が新しくなってるのにパッケージが古いままのような挙動を示す
godepって、なんか挙動にモットーのようなものを感じる*3ので、なんらかの意図があってこうなってるんだと思いますが、まだそういった記述にたどり着けてないです。
現場からは以上です
DRY
追記 2015/02/12
なんかknown issueだった模様。
*1:この際、"directory "xxx" is not using a known version control system"などと怒られるのは、このアプリケーションがgitなどによって管理されていない(.gitが無い)場合です
*2:この際、"godep: cannot find package "foo" in any of"などと怒られるのは、このマシンの$GOPATHにfooパッケージが無いからです
*3:たとえば、godep restoreしたときは必ずリモートレポジトリをgo getしに行くのであって、ローカルのGodeps/_workspace/から$GOPATHへrestoreするようなことはない、とか