The Go Blog - Using Go Modules / Go Modulesを使う（和訳）

この記事は [The Go Blog - Using Go Modules](https://blog.golang.org/using-go-modules) の和訳です。

## はじめに

Go 1.11および1.12には、依存バージョン情報を明示的で管理しやすくする、Goの [新しい依存管理システム](https://blog.golang.org/versioning-proposal) である [モジュール](https://golang.org/doc/go1.11#modules) の予備サポートが含まれています。このブログ記事は、モジュールを使い始めるために必要な基本的な操作を紹介するチュートリアルです。この次の記事では、他の人が使用するためのモジュールのリリース方法について説明します。

モジュールは、そのルートに `go.mod` ファイルを持つファイルツリーに格納された [Goパッケージ](https://golang.org/ref/spec#Packages) の集まりです。 `go.mod` ファイルは、モジュールのモジュールパス（ルートディレクトリに使用されるインポートパス）とその依存関係の要件（ビルドを成功させるために必要な他のモジュール）も定義します。各依存関係の要件は、モジュールパスと特定の [セマンティックバージョン](https://semver.org/) として記述されています。

Go 1.11以降、goコマンドは、カレントディレクトリまたは任意の親ディレクトリに `$GOPATH/src` の外側にある `go.mod` がある場合にモジュールを使用できるようにします。 （`$GOPATH/src` 内では、互換性のため、`go.mod` が見つかってもgoコマンドは古いGOPATHモードで実行されます。詳細については [goコマンドのドキュメント](https://golang.org/cmd/go/#hdr-Preliminary_module_support) を参照してください。）Go 1.13以降は、モジュールモードがデフォルトになります。

この記事では、モジュールを使用してGoコードを開発するときに発生する一連の一般的な操作について説明します。

- [新しいモジュールの作成](#新しいモジュールの作成)
- [依存関係の追加](#依存関係の追加)
- [依存関係の更新](#依存関係の更新)
- [新しいメジャーバージョンへの依存関係の追加](#新しいメジャーバージョンへの依存関係の追加)
- [依存関係の新しいメジャーバージョンへのアップグレード](#依存関係の新しいメジャーバージョンへのアップグレード)
- [未使用の依存関係の削除](#未使用の依存関係の削除)

## 新しいモジュールの作成

新しいモジュールを作成しましょう。

`$GOPATH/src` の外に新しい空のディレクトリを作成し、そのディレクトリに `cd` して、新しいファイル `hello.go` を作成します:

```hello.go
package hello

func Hello() string {
    return "Hello, world."
}
```

`hello_test.go` にテストも書きましょう:

```hello_test.go
package hello

import "testing"

func TestHello(t *testing.T) {
    want := "Hello, world."
    if got := Hello(); got != want {
        t.Errorf("Hello() = %q, want %q", got, want)
    }
}
```

この時点では、ディレクトリにはパッケージが含まれていますが、モジュールは含まれていません。これは、`go.mod` ファイルがないためです。`/home/gopher/hello` で作業していて、`go test` を実行したとしたら、次のようになります:

```console
$ go test
PASS
ok      _/home/gopher/hello    0.020s
$
```

最後の行はパッケージ全体のテストのサマリです。私たちは `$GOPATH` の外で作業しており、どのモジュール内にも入っていないため、goコマンドは現在のディレクトリのインポートパスを知らず、ディレクトリ名から見せかけのパス `_/home/gopher/hello` を作成します。

`go mod init` を使用して現在のディレクトリをモジュールのルートにしてから、もう一度 `go test` を試してみましょう:

```console
$ go mod init example.com/hello
go: creating new go.mod: module example.com/hello
$ go test
PASS
ok      example.com/hello    0.020s
$
```

おめでとうございます！はじめてのモジュールの作成とテストができました。

`go mod init` コマンドは `go.mod` ファイルを生成します:

```console
$ cat go.mod
module example.com/hello

go 1.12
$
```

`go.mod` ファイルはモジュールのルートにのみ現れます。 サブディレクトリ内のパッケージには、モジュールパスとサブディレクトリへのパスで構成されるインポートパスがあります。 たとえば、サブディレクトリworldを作成した場合、そこに `go mod init` を実行する必要はありません（むしろ、するべきではありません）。パッケージは自動的に `example.com/hello` モジュールの一部として認識され、インポートパスは `example.com/hello/world` になります。

## 依存関係の追加

Goモジュールの主な動機は、他の開発者によって書かれたコードを使用する（つまり、依存関係を追加する）経験を向上させることでした。

`hello.go` を更新して `rsc.io/quote` をインポートし、`Hello` の実装に使用しましょう:

```hello.go
package hello

import "rsc.io/quote"

func Hello() string {
    return quote.Hello()
}
```

テストを再実行しましょう:

```console
$ go test
go: finding rsc.io/quote v1.5.2
go: downloading rsc.io/quote v1.5.2
go: extracting rsc.io/quote v1.5.2
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: downloading rsc.io/sampler v1.3.0
go: extracting rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
PASS
ok      example.com/hello    0.023s
$
```

goコマンドは、`go.mod` にリストされている特定の依存関係モジュールのバージョンを使用してインポートを解決します。 `go.mod` のどのモジュールからも提供されていないパッケージのインポートを検出すると、goコマンドは自動的にそのパッケージを含むモジュールを検索し、最新バージョンを使ってそれを `go.mod` に追加します。 （「最新」は、最新のタグ付き安定版（[プレリリース](https://semver.org/#spec-item-9)版ではない）、または最新のタグ付きプレリリース版、または最新のタグなし版として定義されています。） 
この例では、`go test` によって新しいインポート `rsc.io/quote` がモジュール `rsc.io/quote v1.5.2` に解決されました。 また、`rsc.io/quote` によって使用される2つの依存関係、すなわち `rsc.io/sampler` と `golang.org/x/text` もダウンロードしました。
直接の依存関係だけが `go.mod` ファイルに記録されます:

```console
$ cat go.mod
module example.com/hello

go 1.12

require rsc.io/quote v1.5.2
$
```

2回目の `go test` コマンドでは、この作業は繰り返されません。 `go.mod` が最新のものであり、ダウンロードされたモジュールがローカル（`$GOPATH/pkg/mod`）にキャッシュされているためです:

```console
$ go test
PASS
ok      example.com/hello    0.020s
$
```

goコマンドを使用すると新しい依存関係をすばやく簡単に追加できますが、コストがかからないわけではありません。あなたのモジュールは、正当性、セキュリティ、適切なライセンスなどの重要な分野で新しい依存関係に依存しています。その他の考慮事項については、Russ Coxのブログ投稿 [Our Software Dependency Problem](https://research.swtch.com/deps) を参照してください。

上で見たように、一つの直接の依存関係を追加することはしばしば他の間接的な依存関係ももたらします。 `go list -m all` コマンドは、現在のモジュールとそのすべての依存関係を一覧表示します:

```console
$ go list -m all
example.com/hello
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
$
```

`go list` の出力では、現在のモジュールは *メインモジュール* とも呼ばれ、常に最初の行に表示され、その後にモジュールパスでソートされた依存関係が続きます。

`golang.org/x/text` のバージョン `v0.0.0-20170915032832-14c0d48ead0c` は、特定のタグなしコミットに対する goコマンドのバージョン構文である、[疑似バージョン](https://golang.org/cmd/go/#hdr-Pseudo_versions)の例です。

`go.mod` に加えて、goコマンドは特定のモジュールバージョンの内容の予想される[暗号化ハッシュ](https://golang.org/cmd/go/#hdr-Module_downloading_and_verification)を含む `go.sum` という名前のファイルを保持します。

```console
$ cat go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZO...
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:Nq...
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3...
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPX...
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/Q...
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9...
$
```

goコマンドは `go.sum` ファイルを使用して、これらのモジュールの将来のダウンロードが最初のダウンロードと同じビットを取得するようにして、プロジェクトが依存しているモジュールが悪意、偶然、またはその他の理由で予期せず変更されないようにします。`go.mod` と `go.sum` の両方をバージョン管理するべきでしょう。

## 依存関係の更新

Goモジュールでは、バージョンはセマンティックバージョンタグで参照されます。 セマンティックバージョンには、メジャー、マイナー、パッチの3つの部分があります。 たとえば v0.1.2の場合、メジャーバージョンは0、マイナーバージョンは1、パッチバージョンは2です。いくつかのマイナーバージョンアップグレードを見てみましょう。 次のセクションでは、メジャーバージョンのアップグレードを検討します。

`go list -m all` の出力から、タグなしバージョンの `golang.org/x/text` を使用していることがわかります。 最新のタグ付きバージョンにアップグレードして、すべてがまだ機能することをテストしましょう。

```console
$ go get golang.org/x/text
go: finding golang.org/x/text v0.3.0
go: downloading golang.org/x/text v0.3.0
go: extracting golang.org/x/text v0.3.0
$ go test
PASS
ok      example.com/hello    0.013s
$
```

やったね！ ʕ◔ϖ◔ʔ すべて通りました。 `go list -m all` と `go.mod` ファイルをもう一度見てみましょう:

```console
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
$ cat go.mod
module example.com/hello

go 1.12

require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote v1.5.2
)
$
```

`golang.org/x/text` パッケージが最新のタグ付きバージョン（v0.3.0）にアップグレードされました。 `go.mod` ファイルもv0.3.0を指定するように更新されました。 `indirect` コメントは、依存関係がこのモジュールによって直接使用されず、他のモジュール依存関係によって間接的にのみ使用されることを示します。 詳細は `go help modules` を見てください。

それでは、`rsc.io/sampler` マイナーバージョンをアップグレードしてみましょう。同じように、 `go get` を実行してテストを実行します:

```console
$ go get rsc.io/sampler
go: finding rsc.io/sampler v1.99.99
go: downloading rsc.io/sampler v1.99.99
go: extracting rsc.io/sampler v1.99.99
$ go test
--- FAIL: TestHello (0.00s)
    hello_test.go:8: Hello() = "99 bottles of beer on the wall, 99 bottles of beer, ...", want "Hello, world."
FAIL
exit status 1
FAIL    example.com/hello    0.014s
$
```

うっ。テスト失敗は `rsc.io/sampler` の最新版が私達の用法と互換性がないことを示します。 このモジュールの利用可能なタグ付きバージョンをリストしましょう:

```console
$ go list -m -versions rsc.io/sampler
rsc.io/sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99
$
```

私たちは以前v1.3.0を使っていました。v1.99.99はダメそうです。ためしにv1.3.1を使ってみましょう:

```console
$ go get rsc.io/sampler@v1.3.1
go: finding rsc.io/sampler v1.3.1
go: downloading rsc.io/sampler v1.3.1
go: extracting rsc.io/sampler v1.3.1
$ go test
PASS
ok      example.com/hello    0.022s
$
```

`go get` 引数の明示的な `@v1.3.1` に注意してください。 一般的に `go get` に渡される各引数は明示的なバージョンを取ることができます。 デフォルトは `@latest` で、これは以前に定義されたとおりの最新バージョンに解決されます。

## 新しいメジャーバージョンへの依存関係の追加

パッケージに新しい関数を追加しましょう。`Proverb`関数は、`rsc.io/quote/v3` モジュールで提供される`quote.Concurrency` を呼び出すことによって、Goの並行処理に関する金言を返します。 まず、`hello.go` を更新して新しい関数を追加します:

```hello.go
package hello

import (
    "rsc.io/quote"
    quoteV3 "rsc.io/quote/v3"
)

func Hello() string {
    return quote.Hello()
}

func Proverb() string {
    return quoteV3.Concurrency()
}
```

次に `hello_test.go` にテストを追加します:

```hello_test.go
func TestProverb(t *testing.T) {
    want := "Concurrency is not parallelism."
    if got := Proverb(); got != want {
        t.Errorf("Proverb() = %q, want %q", got, want)
    }
}
```

テストしてみましょう:

```console
$ go test
go: finding rsc.io/quote/v3 v3.1.0
go: downloading rsc.io/quote/v3 v3.1.0
go: extracting rsc.io/quote/v3 v3.1.0
PASS
ok      example.com/hello    0.024s
$
```

今回のモジュールは `rsc.io/quote` と `rsc.io/quote/v3` の両方に依存していることに注意してください:

```console
$ go list -m rsc.io/q...
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
$
```

Goモジュールの異なるメジャーバージョン（v1、v2など）はそれぞれ異なるモジュールパスを使用します。v2以降は、パスはメジャーバージョンで終わる必要があります。この例では、`rsc.io/quote` のv3は `rsc.io/quote` ではなくなり、`rsc.io/quote/v3` で識別されます。この規則は[セマンティックインポートのバージョン管理](https://research.swtch.com/vgo-import)と呼ばれ、互換性のないパッケージ（異なるメジャーバージョンを持つもの）には異なる名前を付けます。対照的に、`rsc.io/quote` のv1.6.0はv1.5.2と後方互換性があるはずなので、`rsc.io/quote` という名前を再利用します。（前のセクションでは、`rsc.io/sampler` v1.99.99は `rsc.io/sampler`  v1.3.0と下位互換性がある*はず*でしたが、モジュールの動作に関するバグやクライアントの誤った仮定が発生することもあります。）

goコマンドでは、ビルドで特定のモジュールパスから最大1つ（各メジャーバージョンから最大1つ）を含めることができます: `io/quote` を一つ、`io/quote/v2` を一つ、`io/quote/v3` を一つ... など。これにより、モジュール作成者は単一のモジュールパスの重複の可能性について明確な規則を得ることができます。プログラムが　`rsc.io/quote v1.5.2` と `rsc.io/quote v1.6.0` の両方でビルドすることは不可能です。同時に、モジュールのメジャーバージョンが異なると（パスが異なるため）、モジュール利用者は新しいメジャーバージョンに段階的にアップグレードすることができます。この例では、`rsc/quote/v3 v3.1.0` の `quote.Concurrency` を使用したいのですが、`rsc.io/quote v1.5.2` の使用を移行する準備はまだできていません。段階的に移行する機能は、大規模なプログラムやコードベースでは特に重要です。

## 依存関係の新しいメジャーバージョンへのアップグレード

`rsc.io/quote` から `rsc.io/quote/v3` のみの使用への変換を完了させましょう。 メジャーバージョンの変更により、一部のAPIは削除、名前変更、または互換性のない方法で変更された可能性があります。 ドキュメントを読むと、`Hello` が `HelloV3` になったことがわかります。

```console
$ go doc rsc.io/quote/v3
package quote // import "rsc.io/quote"

Package quote collects pithy sayings.

func Concurrency() string
func GlassV3() string
func GoV3() string
func HelloV3() string
func OptV3() string
$
```

（出力では[既知のバグ](https://github.com/golang/go/issues/30778)により、表示されたインポートパスより `/v3` が誤って削除されています。）

`hello.go` で `quote.Hello()` の使用部分を `quoteV3.HelloV3()` に更新できます。

```hello.go
package hello

import quoteV3 "rsc.io/quote/v3"

func Hello() string {
    return quoteV3.HelloV3()
}

func Proverb() string {
    return quoteV3.Concurrency()
}
```

この時点で、名前を変更したインポートはもう必要ないので、元に戻すことができます:

```hello.go
package hello

import "rsc.io/quote/v3"

func Hello() string {
    return quote.HelloV3()
}

func Proverb() string {
    return quote.Concurrency()
}
```

テストを再実行して、すべてが機能していることを確認しましょう:

```console
$ go test
PASS
ok      example.com/hello       0.014s
```

## 未使用の依存関係の削除

`rsc.io/quote` の使用をすべて削除しましたが、それでも `go list -m all` および `go.mod` には表示されます。

```console
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1
$ cat go.mod
module example.com/hello

go 1.12

require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote v1.5.2
    rsc.io/quote/v3 v3.0.0
    rsc.io/sampler v1.3.1 // indirect
)
$
```

なぜでしょう？それは `go build` や `go test` のように単一のパッケージをビルドする際、何かが足りなくなって追加する必要があるときは簡単にわかりますが、安全に何かを削除できるときはわからないからです。 依存関係の削除は、モジュール内のすべてのパッケージ、およびそれらのパッケージに対して可能なすべてのビルドタグの組み合わせを確認した後にのみ実行できます。 通常のビルドコマンドではこの情報は読み込まれないため、依存関係を安全に削除することはできません。

`go mod tidy` コマンドは、これらの未使用の依存関係をクリーンアップします:

```console
$ go mod tidy
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1
$ cat go.mod
module example.com/hello

go 1.12

require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote/v3 v3.1.0
    rsc.io/sampler v1.3.1 // indirect
)

$ go test
PASS
ok      example.com/hello    0.020s
$
```

## まとめ

GoモジュールはGoの依存関係管理の未来です。 モジュール機能は現在、サポートされているすべてのGoバージョン（つまり、Go 1.11およびGo 1.12）で使用可能です。

この記事では、Goモジュールを使用してこれらのワークフローを紹介しました。

- `go mod init` は新しいモジュールを作成し、それを記述する `go.mod` ファイルを初期化します。
- `go build` や `go test`、およびその他のパッケージビルドを行うコマンドは、必要に応じて `go.mod` に新しい依存関係を追加します。
- `go list -m all` は現在のモジュールの依存関係を表示します。
- `go get` は依存関係の、必要なバージョンを更新（または追加）します。
- `go mod tidy` は未使用の依存関係を削除します。

私達は、あなたがローカルでの環境でモジュールを使い始め、`go.mod`  と `go.sum` を追加することをお勧めします。 Goの依存関係管理の将来を形作るのを助けるために、ぜひバグレポートや経験レポートを送ってください。

フィードバックを提供し、モジュールの改善にご協力いただき、ありがとうございます。

By Tyler Bui-Palsulich and Eno Compton