> Source: https://www.ymotongpoo.com/works/goblog-ja/go116-module-changes/


# Go 1.16におけるモジュールの変更点

[New module changes in Go 1.16](https://go.dev/blog/go116-module-changes) by Jay Conrod

Go 1.16をお楽しみいただけていることと思います。今回のリリースには多くの新機能があり、特にモジュール関連の機能が充実しています。[リリースノート](https://go.dev/doc/go1.16)ではこれらの変更点を簡単に説明していますが、ここではいくつかの機能を詳しく見ていきましょう。

## モジュールがデフォルトで有効に

`go` コマンドは、`go.mod` が存在しない場合でも、デフォルトでモジュール対応モードでパッケージをビルドするようになりました。これはすべてのプロジェクトでモジュールを使うようにするための大きな一歩です。

`GO111MODULE` 環境変数を `off` に設定すれば、これまで通りGOPATHモードでパッケージをビルドすることも可能です。また `GO111MODULE` を `auto` に設定すると、カレントディレクトリまたはその親ディレクトリのいずれかに `go.mod` ファイルが存在する場合にのみモジュール対応モードを有効にできます。これは以前のデフォルトの挙動でした。なお、`go env -w` を使うことで、`GO111MODULE` や他の変数を永続的に設定できることに注意してください。

```
go env -w GO111MODULE=auto
```

私たちはGo 1.17でGOPATHモードのサポートを打ち切る計画です。つまり、Go 1.17では `GO111MODULE` は無視されるようになります。もしモジュール対応モードでビルドできないプロジェクトをお持ちなら、今が移行のタイミングです。移行を妨げる問題がある場合は、[issue](https://go.dev/issue/new)や[experience report](https://go.dev/wiki/ExperienceReports)の提出をご検討ください。

## go.modとgo.sumへの自動的な変更の廃止

以前は、`go` コマンドが `go.mod` や `go.sum` の問題（`require` ディレクティブの欠落やチェックサムの欠落など）を検出すると、その問題を自動的に修正しようとしていました。この挙動は、特に通常は副作用を持たないはずの `go list` のようなコマンドにおいて予期しないものであるという声を多くいただきました。この自動修正は必ずしも望ましいものではありませんでした。もしインポートされたパッケージがどの必須モジュールにも提供されていない場合、`go` コマンドは新しい依存関係を追加し、場合によっては共通の依存関係のアップグレードを引き起こしてしまうことがありました。インポートパスのスペルミスでさえ、失敗に終わるネットワーク検索を引き起こしていました。

Go 1.16では、モジュール対応のコマンドは `go.mod` や `go.sum` の問題を発見すると、それを自動的に修正しようとするのではなく、エラーを報告するようになりました。多くの場合、エラーメッセージには問題を修正するためのコマンドが提示されます。

```
$ go build
example.go:3:8: no required module provides package golang.org/x/net/html; to add it:
    go get golang.org/x/net/html
$ go get golang.org/x/net/html
$ go build
```

これまでと同様に、`go` コマンドは `vendor` ディレクトリが存在すればそれを使用します（詳細は[Vendoring](https://go.dev/ref/mod#vendoring)を参照してください）。`go get` や `go mod tidy` といったコマンドは、その主な目的が依存関係の管理であるため、引き続き `go.mod` と `go.sum` を変更します。

## 特定バージョンの実行可能ファイルのインストール

`go install` コマンドは、`@version` サフィックスを指定することで、特定バージョンの実行可能ファイルをインストールできるようになりました。

```
go install golang.org/x/tools/gopls@v0.6.5
```

この構文を使うと、`go install` はカレントディレクトリや親ディレクトリにある `go.mod` ファイルを無視して、指定した正確なモジュールバージョンからコマンドをインストールします。（`@version` サフィックスを指定しない場合、`go install` はこれまで通り、カレントモジュールの `go.mod` に記載されているバージョン要件とリプレースを使ってプログラムをビルドします。）

以前は実行可能ファイルをインストールするために `go get -u program` を推奨していましたが、この用法は `go.mod` 内のモジュールバージョン要件を追加・変更するという `go get` 本来の意味との間で多くの混乱を招いてしまいました。そして `go.mod` を誤って変更してしまわないように、次のようなより複雑なコマンドを提案する人たちも出てきました。

```
cd $HOME; GO111MODULE=on go get program@latest
```

これからは `go install program@latest` を使えばよいのです。詳細は[`go install`](https://go.dev/ref/mod#go-install)を参照してください。

どのバージョンが使われるかについての曖昧さを排除するため、このインストール構文を使う際にプログラムの `go.mod` ファイルに記述できるディレクティブにはいくつかの制約があります。特に、少なくとも現時点では `replace` ディレクティブと `exclude` ディレクティブは許可されていません。長期的には、新しい `go install program@version` が十分に多くのユースケースでうまく機能するようになった時点で、`go get` がコマンドバイナリのインストールを行わないようにする計画です。詳細は[issue 43684](https://go.dev/issue/43684)を参照してください。

## モジュールのリトラクション（取り消し）

準備が整う前にうっかりモジュールのバージョンを公開してしまった経験はありませんか。あるいは、バージョンを公開した直後に、早急に修正が必要な問題を発見したことは。公開されたバージョンの間違いを訂正するのは難しいものです。モジュールのビルドを決定的なものにし続けるため、一度公開されたバージョンはその後変更できません。たとえバージョンタグを削除したり変更したりしても、[`proxy.golang.org`](https://proxy.golang.org)や他のプロキシは、すでに元のバージョンをキャッシュしている可能性が高いのです。

モジュールの作者は、`go.mod` 内の `retract` ディレクティブを使って、モジュールのバージョンを**リトラクト**（取り消し）できるようになりました。リトラクトされたバージョンは引き続き存在し、ダウンロードもできます（そのため、そのバージョンに依存しているビルドが壊れることはありません）が、`go` コマンドは `@latest` のようなバージョン解決の際にそのバージョンを自動的には選択しなくなります。`go get` や `go list -m -u` は、既存の利用について警告を表示します。

たとえば、人気のライブラリ `example.com/lib` の作者が `v1.0.5` をリリースした後、新たなセキュリティ上の問題を発見したとしましょう。作者は次のようなディレクティブを `go.mod` ファイルに追加できます。

```
// Remote-triggered crash in package foo. See CVE-2021-01234.
retract v1.0.5
```

次に、作者は新しい最高バージョンとなる `v1.0.6` にタグを付けてプッシュします。これ以降、すでに `v1.0.5` に依存しているユーザーは、更新を確認したときや依存先のパッケージをアップグレードしたときに、リトラクションについて通知されます。この通知メッセージには、`retract` ディレクティブの上に書かれたコメントの内容が含まれることがあります。

```
$ go list -m -u all
example.com/lib v1.0.0 (retracted)
$ go get .
go: warning: example.com/lib@v1.0.5: retracted by module author:
    Remote-triggered crash in package foo. See CVE-2021-01234.
go: to switch to the latest unretracted version, run:
    go get example.com/lib@latest
```

インタラクティブなブラウザベースのガイドについては、[play-with-go.dev](https://play-with-go.dev/)の[Retract Module Versions](https://play-with-go.dev/retract-module-versions_go116_en/)を参照してください。構文の詳細は[`retract` ディレクティブのドキュメント](https://go.dev/ref/mod#go-mod-file-retract)を参照してください。

## GOVCSによるバージョン管理ツールの制御

`go` コマンドは、[proxy.golang.org](https://proxy.golang.org)のようなミラーからモジュールのソースコードをダウンロードすることも、`git`、`hg`、`svn`、`bzr`、`fossil` を使ってバージョン管理リポジトリから直接ダウンロードすることもできます。バージョン管理への直接アクセスは、プロキシから入手できないプライベートモジュールにとって特に重要ですが、それと同時にセキュリティ上の問題にもなり得ます。バージョン管理ツールのバグが、悪意あるサーバーによって意図しないコードの実行に悪用されるかもしれないからです。

Go 1.16では、`GOVCS` という新しい設定変数が導入されました。これにより、どのモジュールがどのバージョン管理ツールの使用を許可されるかをユーザーが指定できます。`GOVCS` はカンマ区切りの `pattern:vcslist` ルールのリストを受け取ります。`pattern` はモジュールパスの先頭の1つ以上の要素にマッチする[`path.Match`](https://go.dev/pkg/path#Match)パターンです。特別なパターンである `public` と `private` は、それぞれパブリックモジュールとプライベートモジュールにマッチします（`private` は `GOPRIVATE` のパターンにマッチするモジュールとして定義され、`public` はそれ以外のすべてです）。`vcslist` は、許可されるバージョン管理コマンドのパイプ区切りのリスト、またはキーワード `all` か `off` です。

たとえば、次のように設定します。

```
GOVCS=github.com:git,evil.com:off,*:git|hg
```

この設定では、`github.com` 上のパスを持つモジュールは `git` を使ってダウンロードでき、`evil.com` 上のパスはどのバージョン管理コマンドを使ってもダウンロードできず、それ以外のすべてのパス（`*` はすべてにマッチします）は `git` または `hg` を使ってダウンロードできます。

`GOVCS` が設定されていない場合、あるいはモジュールがどのパターンにもマッチしない場合、`go` コマンドは次のデフォルト設定を使います。パブリックモジュールには `git` と `hg` が許可され、プライベートモジュールにはすべてのツールが許可されます。GitとMercurialのみを許可している理由は、この2つのシステムが、信頼できないサーバーのクライアントとして実行される際の問題に最も注意が払われてきたからです。それに対して、Bazaar、Fossil、Subversionは主に信頼された認証済みの環境で使われてきており、攻撃対象領域としてはそれほど精査されていません。つまり、デフォルトの設定は次の通りです。

```
GOVCS=public:git|hg,private:all
```

詳細は[GOVCSによるバージョン管理ツールの制御](https://go.dev/ref/mod#vcs-govcs)を参照してください。

## 今後の展望

これらの機能がお役に立てば幸いです。私たちはすでにGo 1.17に向けた次のモジュール機能群に着手しており、特に[遅延モジュールロード（lazy module loading）](https://go.dev/issue/36460)に取り組んでいます。これにより、モジュールのロード処理がより高速で安定したものになるはずです。いつものように、新しいバグに遭遇した場合は[issueトラッカー](https://github.com/golang/go/issues)でお知らせください。それでは、良いコーディングを。

By Jay Conrod

