プロジェクト作成・依存関係管理(Go Modules)
Goではコードのバージョン管理や依存関係管理の仕組みとしてモジュール(Go Modules)があります。
ここではモジュールを使って次の内容を説明します。
- Goのプロジェクト作成(=モジュールの作成)
- モジュールの依存関係の管理
- ローカルマシン上にある自作モジュールの利用
- プライベートリポジトリ上にある自作モジュールの利用
このページはGo1.13以降の利用を前提とします。
モジュールとは
Goのモジュールは、ルートディレクトリにgo.mod
ファイルを含む、Goのパッケージの集合です。
モジュールはパッケージ群のバージョン管理や配布をする単位となります。
バージョンの付け方はセマンティックバージョニングに従います。
モジュールの管理単位としては、基本的に1リポジトリ1モジュールで管理します(Go開発者の一人であるRuss Coxさんが推奨)。 このため、Goのプロジェクトを作成することは、モジュールを作成することと捉えることができます。
モジュールの作成
モジュールを作成する前に、モジュールが包含するパッケージを作成します。
任意のディレクトリを作成します。
mkdir $HOME/calc
cd $HOME/calc
calc.go
を作成します。
package calc
func Max(x, y int) int {
if x < y {
return y
}
return x
}
calc_test.go
にテストを書きます。
package calc
import "testing"
func TestMax(t *testing.T) {
want := 2
got := Max(1, 2)
if want != got {
t.Errorf("Max(1, 2) == %d, want %d", got, want)
}
}
この時点では、このディレクトリは単なるパッケージであり、モジュールではありません。
go.mod
がまだないためです。
そのことを確認するために、次の通りテストを実行します。
go test
コマンドはインポートパスがわからず、
ローカルのパスを基にした偽のインポートパス(筆者の環境では_/home/go/calc
)を表示しています。
go test
PASS
ok _/home/go/calc 0.100s
go mod init
コマンドを使ってgo.mod
を作成し、現在のディレクトリをモジュールのルートとします。
go mod init example.com/calc
go: creating new go.mod: module example.com/calc
go.mod
には、モジュールの場所を示すモジュールパスが定義されます。
cat go.mod
module example.com/calc
go 1.15
モジュールを作成したので、もう一度go test
を実行します。
すると、先ほど定義したモジュールパスexample.com/calc
がインポートパスとして表示されます。
go test
PASS
ok example.com/calc 0.100s
これでモジュールの作成は完了です。
今回、モジュールの場所を示すモジュールパスはexample.com/calc
としました。
モジュールを外部に公開する場合は、公開先のURLにします。
例としてGitHubに公開する場合、github.com/<user>/<repo>
のようにします。
モジュールを公開しない場合は、calc
のようにシンプルな名前でも大丈夫です。
モジュールには複数のパッケージを配置できます。
パッケージを追加するにはサブディレクトリを作成します。
サブディレクトリのインポートパスは、モジュールパスとサブディレクトリパスを結合したものです。
次のディレクトリ構成の場合、pkg1
パッケージのインポートパスはexample.com/calc/pkg1
になります。
サブディレクトリに別途go.mod
を作成する必要はありません。
モジュールパス:example.com/calc
calcディレクトリ(パッケージ:calc、インポートパス:example.com/calc)
├── go.mod(モジュールのバージョンを管理するファイル)
├── go.sum(利用モジュールのsum値を管理するファイル)
├── calc.go(calcパッケージのソースファイル)
├── calc_test.go(calcパッケージのソースファイル)
└── pkg1ディレクトリ(パッケージ:pkg1、インポートパス:example.com/calc/pkg1)
├── pkg1.go(pkg1パッケージのソースファイル)
└── pkg1_test.go(pkg1パッケージのソースファイル)
依存関係の追加
見出し『モジュールの作成』で作成したモジュールに依存関係を追加する方法を説明します。
calc.go
を次のように変更します。
package calc
import (
"github.com/google/go-cmp/cmp"
)
func Max(x, y int) int {
if x < y {
return y
}
return x
}
func Equal(x, y int) bool {
return cmp.Equal(x, y)
}
go mod tidy
コマンドを使って必要な依存関係(github.com/google/go-cmp/cmp
)を追加します。
このコマンドは、次の処理をします。
- ソースコードのimport文を参照し、必要なモジュールを
go.mod
に追加する - 追加されたモジュールをダウンロードする
- コードの変更に伴い、不要なモジュールを
go.mod
から削除する
cd $HOME/calc
go mod tidy
go: finding module for package github.com/google/go-cmp/cmp
go: downloading github.com/google/go-cmp v0.5.4
go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.5.4
go: downloading golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
go.mod
に依存関係が追加されたことを確認します。
バージョンは最新のものが使用されます。
github.com/google/go-cmp/cmp
の依存関係としてgolang.org/x/xerrors
もダウンロードされましたが、
go.mod
には直接の依存関係のみが記録されます。
cat go.mod
module example.com/calc
go 1.15
require github.com/google/go-cmp v0.5.4
直接の依存関係と間接の依存関係の両方を確認するにはgo list
コマンドを実行します。
go list -m all
example.com/calc
github.com/google/go-cmp v0.5.4
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
go
コマンドは、go.mod
に加えて、go.sum
にモジュールのバージョンとハッシュ値を記録します。
cat go.sum
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
一度ダウンロードしたモジュールはローカルマシン上にキャッシュされます。
何らかの理由でキャッシュを削除するにはgo clean
コマンドを実行します。
go clean -modcache
これで依存関係の追加が完了しました。
gitなどのバージョン管理にはgo.mod
とgo.sum
の両方をコミットしてください。
依存関係の更新(メジャーバージョン以外)
go list
コマンドに-u
オプションを指定して、アップグレード可能なモジュールを確認します。
cd $HOME/calc
go list -m -u all
アップグレード可能なバージョンが角括弧で表示されます。
example.com/calc
github.com/google/go-cmp v0.5.3 [v0.5.4]
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 [v0.0.0-20200804184101-5ec99f83aff1]
特定のモジュールをアップグレードするにはgo get
コマンドでモジュールを指定して実行します。
go get github.com/google/go-cmp
モジュールパスの後に@
を続けて、モジュールのバージョンを指定できます。
go get github.com/google/go-cmp@v0.5.4
すべてのモジュールをアップグレードするには./...
と指定します。
go get -u ./...
依存関係の更新(メジャーバージョン)
モジュールは、メジャーバージョンが変わるとモジュールパスが変わります(v0からv1は例外で変わりません)。 セマンティックバージョニングではメジャーバージョンが変わると、後方互換性がなくなるためです。
モジュールパスは末尾にメジャーバージョンをつけます(v0とv1は例外でつけません)。
執筆時点では存在しませんが、仮にgithub.com/google/go-cmp
をv1からv2にする場合は、モジュールパスの末尾にv2
を付加します。
import (
"github.com/google/go-cmp/v2/cmp"
)
go getもモジュールパスにv2をつけます。
cd $HOME/calc
go get github.com/google/go-cmp/v2
モジュールパスの後に@
を続けて、モジュールのバージョンを指定できます。
go get github.com/google/go-cmp/v2@v2.0.1
メジャーバージョンが異なるモジュールは同時に利用できます。 たとえば、v1からv2に徐々に移行する場合は、次のように両方をインポートします。
import (
"github.com/google/go-cmp/cmp"
cmpV2 "github.com/google/go-cmp/v2/cmp"
)
まとめると次の通りです。
- メジャーバージョンが変わるとモジュールパスが変わる(v0からv1は例外で変わらない)
- モジュールパスは末尾にメジャーバージョンをつける(v0とv1は例外でバージョンをつけない)
- メジャーバージョンが異なる場合は同時に利用(インポート)できる
自作モジュールの利用(ローカル)
見出し『モジュールの作成』で作成したローカルマシン上のモジュールを利用する方法を説明します。
任意のディレクトリを作成して、モジュールを作成します。
mkdir $HOME/myapp
cd $HOME/myapp
go mod init example.com/myapp
main.go
を作成します。
package main
import (
"fmt"
"example.com/calc"
)
func main() {
fmt.Println(calc.Max(1, 2))
}
公開されているモジュールはモジュールパスが公開先のURLであることから、go
コマンドが自動的に参照できました。
しかし、ローカルマシン上のモジュールは場所がわかりません。
そのため、go
コマンドにモジュールの場所を知らせる必要があります。
go mod edit
コマンドを使ってgo.mod
にモジュールの場所を定義します。
-replace
オプションの値はモジュールパス=ディレクトリ
の形式で指定します。
筆者の環境ではモジュールパスexample.com/calc
の場所は/home/go/calc
ディレクトリと定義しました。
利用している環境にあわせて読み替えてください。
go mod edit -replace "example.com/calc=/home/go/calc"
cat go.mod
module example.com/myapp
go 1.15
replace example.com/calc => /home/go/calc
go mod tidy
コマンドを使って依存関係にexample.com/calc
を追加します。
go mod tidy
cat go.mod
module example.com/myapp
go 1.15
replace example.com/calc => /home/go/calc
require example.com/calc v0.0.0-00010101000000-000000000000
プログラムを実行します。
go run .
2
これで自作モジュールを利用したプログラムを作成できました。
自作モジュールの利用(プライベートリポジトリ)
プライベートリポジトリ上のモジュールを利用する方法を説明します。
前提条件は次の通りです。
- GitHubのプライベートリポジトリを利用
go
コマンドはモジュールをダウンロードする際に、デフォルトでプロキシサーバを経由します。
プロキシサーバはプライベートリポジトリの認証情報を知らないため、モジュールをダウンロードできません。
これを回避するには、go
コマンドに対してプロキシサーバを経由しないようにする設定が必要です。
環境変数GOPRIVATE
にプライベートリポジトリのモジュールパス(前方一致)を記載することで、
そのモジュールはプロキシを経由せずにダウンロードできます。
go env -w GOPRIVATE=github.com/<user>
これで、go
コマンドはプライベートリポジトリのモジュールを直接ダウンロードできるようになりました。
次はgo
コマンドがプライベートリポジトリにアクセスするためのGitHub認証情報を設定します。
GitHubのパーソナルアクセストークンを生成し、生成したパーソナルアクセストークンをgitに設定します。
# xxxはパーソナルアクセストークンに読み替える
# <user>はユーザ名に読み替える
git config --global url."https://xxx:x-oauth-basic@github.com/".insteadOf "https://github.com/<user>/"
これでプライベートリポジトリにアクセスする準備が整いました。
あとは通常の外部モジュールを利用するように、go get
やgo mod tidy
などでモジュールを追加してください。