Go Wiki: Go Modules

このWikiページは、使用方法とトラブルシューティングのガイドとして機能します。

  • チュートリアルブログ記事については、「Go Modulesの使用」を参照してください。
  • 技術的なリファレンスについては、「Go Modules Reference」(開発中)を参照してください。

Goは、1.11以降、ここで提案されたバージョン付きモジュールのサポートを含んでいます。初期のプロトタイプvgoは2018年2月に発表されました。2018年7月には、バージョン付きモジュールがGoのメインリポジトリに導入されました。

Go 1.14以降、モジュールサポートは本番環境での使用準備が整ったと見なされており、すべてのユーザーは他の依存関係管理システムからモジュールへの移行が推奨されています。Goツールチェーンに問題があり移行できない場合は、その問題がオープンなイシューとして報告されていることを確認してください。(イシューがGo1.16のマイルストーンにない場合は、移行を妨げる理由についてコメントして、適切に優先順位付けされるようにしてください)。より詳細なフィードバックについては、体験レポートを提供することもできます。

最近の変更点

Go 1.16

詳細については、Go 1.16リリースノートを参照してください。

  • モジュールモード (GO111MODULE=on) がすべての場合でデフォルトになりました
  • コマンドはデフォルトでgo.mod / go.sumを変更しなくなりました (-mod=readonly)
  • go install pkg@versionがパッケージ / 実行可能ファイルをグローバルにインストールする推奨方法です
  • go.modretractが利用可能になりました

Go 1.15

詳細については、Go 1.15リリースノートを参照してください。

  • モジュールキャッシュの場所は、GOMODCACHE環境変数で設定できるようになりました。GOMODCACHEのデフォルト値はGOPATH[0]/pkg/modで、これはこの変更前のモジュールキャッシュの場所でした。
  • Goコマンドがモジュールキャッシュにアクセスする際に、外部プログラムが同時にファイルシステムをスキャンすることによって発生するWindowsの「アクセスが拒否されました」エラー(イシュー#36568参照)の回避策が利用可能になりました。この回避策は、Goバージョン1.14.2より古いバージョンと1.13.10より古いバージョンが同じモジュールキャッシュで同時に実行されている場合には安全ではないため、デフォルトでは有効になっていません。GODEBUG=modcacheunzipinplace=1環境変数を明示的に設定することで有効にできます。

Go 1.14

詳細については、Go 1.14リリースノートを参照してください。

  • メインモジュールにトップレベルのベンダーディレクトリが含まれ、そのgo.modファイルがgo 1.14以上を指定している場合、goコマンドは、そのフラグを受け入れる操作に対してデフォルトで-mod=vendorを使用するようになりました。
  • go.modファイルが読み取り専用で、トップレベルのベンダーディレクトリが存在しない場合、-mod=readonlyがデフォルトで設定されるようになりました。
  • -modcacherwは、goコマンドがモジュールキャッシュに新しく作成されたディレクトリを読み取り専用にする代わりに、デフォルトのパーミッションのままにするように指示する新しいフラグです。
  • -modfile=fileは、goコマンドがモジュールルートディレクトリにあるものとは別のgo.modファイルを読み取る(そして書き込む可能性のある)ように指示する新しいフラグです。
  • モジュール対応モードが明示的に有効になっている場合 (GO111MODULE=onを設定した場合)、go.modファイルが存在しないと、ほとんどのモジュールコマンドの機能が制限されます。
  • goコマンドはモジュールモードでSubversionリポジトリをサポートするようになりました。

Go 1.13

詳細については、Go 1.13リリースノートを参照してください。

  • goツールは、デフォルトでパブリックGoモジュールミラーhttps://proxy.golang.orgからモジュールをダウンロードするようになり、ダウンロードしたモジュール(ソースに関わらず)をパブリックGoチェックサムデータベースhttps://sum.golang.orgに対して検証するようになりました。
    • プライベートコードがある場合は、GOPRIVATE設定(例:go env -w GOPRIVATE=*.corp.com,github.com/secret/repo)または、より細かいGONOPROXYGONOSUMDB(あまり頻繁に使用されないケースをサポート)を設定する必要があります。詳細についてはドキュメントを参照してください。
  • GO111MODULE=autoは、GOPATH内でもgo.modが見つかればモジュールモードを有効にします。(Go 1.13以前では、GO111MODULE=autoはGOPATH内でモジュールモードを有効にすることはありませんでした)。
  • go getの引数が変更されました
    • 引数なしのgo get -uは、現在のパッケージの直接および間接的な依存関係のみをアップグレードし、モジュール全体を検査しなくなりました。
    • モジュールルートからのgo get -u ./...は、モジュールのすべての直接および間接的な依存関係をアップグレードし、テスト依存関係は除外されるようになりました。
    • go get -u -t ./...は似ていますが、テスト依存関係もアップグレードします。
    • go get-mをサポートしなくなりました(他の変更によりgo get -dと大幅に重複するため。通常、go get -m foogo get -d fooで置き換えることができます)。

目次

「クイックスタート」と「新しい概念」のセクションは、モジュールでの作業を開始する人にとって特に重要です。「ハウツー...」のセクションでは、メカニズムについてさらに詳しく説明しています。このページで最も多くのコンテンツは、より具体的な質問に答えるFAQにあります。ここにリストされているFAQの1行を少なくともざっと読む価値はあります。

クイックスタート

詳細は本ページの残りの部分で説明しますが、ここではモジュールをゼロから作成する簡単な例を示します。

GOPATHの外にディレクトリを作成し、必要に応じてVCSを初期化する

$ mkdir -p /tmp/scratchpad/repo
$ cd /tmp/scratchpad/repo
$ git init -q
$ git remote add origin https://github.com/my/repo

新しいモジュールを初期化する

$ go mod init github.com/my/repo

go: creating new go.mod: module github.com/my/repo

コードを書く

$ cat <<EOF > hello.go
package main

import (
    "fmt"
    "rsc.io/quote"
)

func main() {
    fmt.Println(quote.Hello())
}
EOF

ビルドと実行

$ go mod tidy
go: finding module for package rsc.io/quote
go: found rsc.io/quote in rsc.io/quote v1.5.2
$ go build -o hello
$ ./hello
Hello, world.

go.modファイルは、依存関係の明示的なバージョン(ここでv1.5.2semverタグ)を含むように更新されました

$ cat go.mod
module github.com/my/repo

go 1.16

require rsc.io/quote v1.5.2

日常のワークフロー

1.16より前は、go build -o helloを実行する前にgo getgo mod tidyは不要でした。go.modgo.sumファイルの暗黙的な変更は、1.16でデフォルトで無効になりました。

通常の日常ワークフローは次のとおりです。

  • 必要に応じて、.goコードにインポートステートメントを追加します。
  • go buildgo testのような標準コマンドは、インポートを満たすために必要に応じて新しい依存関係を自動的に追加します(go.modを更新し、新しい依存関係をダウンロードします)。
  • 必要に応じて、go get foo@v1.2.3go get foo@master (Mercurialではfoo@default)、go get foo@e3702bed2などのコマンド、またはgo.modを直接編集することで、より具体的な依存関係のバージョンを選択できます。

使用する可能性のあるその他の一般的な機能の簡単な紹介

  • go list -m all — 直接的および間接的なすべての依存関係のビルドで使用される最終バージョンを表示 (詳細)
  • go list -u -m all — 直接的および間接的なすべての依存関係で利用可能なマイナーおよびパッチのアップグレードを表示 (詳細)
  • go get -u ./... または go get -u=patch ./... (モジュールルートディレクトリから) — すべての直接的および間接的な依存関係を最新のマイナーまたはパッチのアップグレードに更新 (プレリリースは無視されます) (詳細)
  • go build ./... または go test ./... (モジュールルートディレクトリから) — モジュール内のすべてのパッケージをビルドまたはテスト (詳細)
  • go mod tidygo.modから不要になった依存関係を削除し、他のOS、アーキテクチャ、およびビルドタグの組み合わせに必要な依存関係を追加します (詳細)
  • replace ディレクティブまたは gohack — 依存関係のフォーク、ローカルコピー、または正確なバージョンを使用 (詳細)
  • go mod vendor — オプションでvendorディレクトリを作成する手順 (詳細)

「新しい概念」に関する次の4つのセクションを読めば、ほとんどのプロジェクトでモジュールを使い始めるのに十分な情報が得られます。また、上記の目次(そこにあるFAQの概要も含む)を確認して、より詳細なトピックのリストに慣れておくことも役立ちます。

新しい概念

これらのセクションでは、主要な新しい概念を概観的に紹介します。詳細と理論的根拠については、設計の背後にある哲学を説明するRuss Coxによる40分の入門ビデオ公式提案ドキュメント、またはより詳細な初期のvgoブログシリーズを参照してください。

モジュール

モジュールとは、関連するGoパッケージのコレクションであり、単一の単位としてまとめてバージョン管理されます。

モジュールは、正確な依存関係要件を記録し、再現可能なビルドを作成します。

ほとんどの場合、バージョン管理リポジトリには、リポジトリルートで定義されたモジュールが正確に1つ含まれています。(単一のリポジトリで複数のモジュールがサポートされていますが、通常はリポジトリごとに1つのモジュールよりも継続的な作業が増えることになります)。

リポジトリ、モジュール、およびパッケージ間の関係の要約

  • リポジトリには、1つ以上のGoモジュールが含まれます。
  • 各モジュールには、1つ以上のGoパッケージが含まれます。
  • 各パッケージは、単一のディレクトリ内の1つ以上のGoソースファイルで構成されます。

モジュールはsemverに従って意味的にバージョン管理される必要があり、通常はv(メジャー).(マイナー).(パッチ)の形式で、たとえばv0.1.0v1.2.3、またはv1.5.0-rc.1のようになります。先頭のvは必須です。Gitを使用している場合は、リリースされたコミットをバージョンでタグ付けします。パブリックおよびプライベートのモジュールリポジトリとプロキシが利用可能になりつつあります(以下のFAQ参照)。

go.mod

モジュールは、ツリーのルートディレクトリにあるgo.modファイルを持つGoソースファイルのツリーによって定義されます。モジュールソースコードはGOPATHの外部に配置できます。modulerequirereplaceexcludeの4つのディレクティブがあります。

モジュールgithub.com/my/thingを定義するgo.modファイルの例を次に示します。

module github.com/my/thing

require (
    github.com/some/dependency v1.2.3
    github.com/another/dependency/v4 v4.0.0
)

モジュールは、moduleディレクティブを通じてgo.modファイル内でそのIDを宣言し、これによりモジュールパスが提供されます。モジュール内のすべてのパッケージのインポートパスは、モジュールパスを共通のプレフィックスとして共有します。モジュールパスとgo.modからパッケージディレクトリへの相対パスが結合されて、パッケージのインポートパスが決定されます。

たとえば、github.com/user/mymod/foogithub.com/user/mymod/barというインポートパスを持つ2つのパッケージを含むリポジトリgithub.com/user/mymodのモジュールを作成している場合、go.modファイルの最初の行は通常、モジュールパスをmodule github.com/user/mymodと宣言し、対応するディスク上の構造は次のようになります。

mymod
|-- bar
|   `-- bar.go
|-- foo
|   `-- foo.go
`-- go.mod

Goソースコードでは、パッケージはモジュールパスを含む完全なパスを使用してインポートされます。たとえば、上記の例で、go.modでモジュールIDをmodule github.com/user/mymodと宣言した場合、コンシューマは次のようにできます。

import "github.com/user/mymod/bar"

これにより、モジュールgithub.com/user/mymodからパッケージbarがインポートされます。

excludeおよびreplaceディレクティブは、現在の(「メイン」)モジュールのみに作用します。メインモジュール以外のモジュールにあるexcludeおよびreplaceディレクティブは、メインモジュールをビルドするときに無視されます。したがって、replaceおよびexcludeステートメントを使用すると、メインモジュールは自身のビルドを完全に制御でき、依存関係による完全な制御を受けることなく済みます。(replaceディレクティブを使用すべき時期については、以下のFAQ参照)。

バージョン選択

ソースコードに新しいインポートを追加し、それがまだgo.modrequireでカバーされていない場合、'go build'や'go test'のようなほとんどのgoコマンドは、適切なモジュールを自動的に探し、その新しい直接的な依存関係の最も新しいバージョンをモジュールのgo.modrequireディレクティブとして追加します。たとえば、新しいインポートが最新タグ付きリリースバージョンがv1.2.3である依存関係Mに対応する場合、モジュールのgo.modrequire M v1.2.3となり、これはモジュールMがバージョン>= v1.2.3(v2はv1と互換性がないと見なされるため、< v2)の依存関係であることを示します。

ビルドで使用されるすべてのモジュールのバージョンを選択するために、最小バージョン選択アルゴリズムが使用されます。ビルド内の各モジュールについて、最小バージョン選択によって選択されるバージョンは、メインモジュールまたはその依存関係のrequireディレクティブによって明示的にリストされているバージョンの中で、常にセマンティックに最も高いバージョンです。

たとえば、あなたのモジュールがrequire D v1.0.0を持つモジュールAに依存し、またあなたのモジュールがrequire D v1.1.1を持つモジュールBにも依存する場合、最小バージョン選択はビルドに含めるDのv1.1.1を選択します(これはリストされているrequireバージョンの中で最も高いからです)。Dのv1.2.0が後で利用可能になったとしても、このv1.1.1の選択は一貫して維持されます。これは、モジュールシステムが100%再現可能なビルドを提供する方法の例です。準備ができたら、モジュールの作成者またはユーザーは、Dの最新バージョンにアップグレードするか、Dの明示的なバージョンを選択することができます。

最小バージョン選択アルゴリズムの簡単な根拠と概要については、公式提案の「高忠実度ビルド」セクション、またはより詳細なvgoブログシリーズを参照してください。

選択されたモジュールバージョン(間接的な依存関係も含む)のリストを表示するには、go list -m allを使用します。

以下の「依存関係のアップグレードとダウングレード方法」セクションと、「バージョンはどのように非互換とマークされますか?」FAQも参照してください。

意味的インポートバージョン管理

長年にわたり、公式のGo FAQにはパッケージのバージョン管理に関するこのアドバイスが含まれていました。

「公開目的のパッケージは、進化するにつれて後方互換性を維持するように努めるべきです。Go 1の互換性ガイドラインは、ここで良い参考になります。エクスポートされた名前を削除せず、タグ付き複合リテラルを推奨するなどです。異なる機能が必要な場合は、古い名前を変更する代わりに新しい名前を追加してください。完全に破壊的な変更が必要な場合は、新しいインポートパスを持つ新しいパッケージを作成してください。」

最後の文は特に重要です。互換性を損なう場合は、パッケージのインポートパスを変更する必要があります。Go 1.11モジュールでは、このアドバイスがインポート互換性ルールとして正式化されています。

「古いパッケージと新しいパッケージが同じインポートパスを持つ場合、新しいパッケージは古いパッケージと後方互換性がなければならない。」

Semverは、v1以上のパッケージが後方互換性のない変更を行う場合、メジャーバージョンを変更する必要があると規定していることを思い出してください。インポート互換性ルールとSemverの両方に従うことの結果は、セマンティックインポートバージョン管理と呼ばれ、メジャーバージョンはインポートパスに含まれます。これにより、互換性の問題によりメジャーバージョンがインクリメントされるたびにインポートパスが変更されることが保証されます。

セマンティックインポートバージョン管理の結果、Goモジュールにオプトインするコードはこれらのルールに準拠する必要があります

  • セマンティックバージョニングに従うこと。(VCSタグの例はv1.2.3です)。
  • モジュールがv2以上のバージョンである場合、モジュールのメジャーバージョンは、go.modファイルで使用されるモジュールパスの末尾に/vNとして(例:module github.com/my/mod/v2require github.com/my/mod/v2 v2.0.1)、およびパッケージインポートパスに(例:import "github.com/my/mod/v2/mypkg")含める必要があります。これにはgo getコマンドで使用されるパスも含まれます(例:go get github.com/my/mod/v2@v2.0.1。この例では/v2@v2.0.1の両方があることに注意してください。モジュール名に/v2が含まれると考えることができます。そのため、モジュールを使用するたびに/v2を含めます)。
  • モジュールがv0またはv1のバージョンである場合、モジュールパスまたはインポートパスのいずれにもメジャーバージョンを含めないでください

一般に、インポートパスが異なるパッケージは異なるパッケージです。たとえば、math/randcrypto/randとは異なるパッケージです。これは、インポートパスに異なるメジャーバージョンが出現している場合も同様です。したがって、example.com/my/mod/mypkgexample.com/my/mod/v2/mypkgとは異なるパッケージであり、両方を単一のビルドでインポートできます。これにより、ダイヤモンド依存関係の問題が解決されるなどの利点があり、v1モジュールがv2の後継モジュールとして実装されたり、その逆も可能になります。

セマンティックインポートバージョン管理の詳細については、goコマンドドキュメントの「モジュールの互換性とセマンティックバージョン管理」セクションを、セマンティックバージョン管理の詳細についてはhttps://semver.orgを参照してください。

これまでのこのセクションでは、モジュールにオプトインし、他のモジュールをインポートするコードに焦点を当ててきました。しかし、v2+モジュールのインポートパスにメジャーバージョンを含めることは、Goの古いバージョンやまだモジュールにオプトインしていないコードとの間で非互換性を生み出す可能性があります。これを助けるために、上記で説明した動作とルールには、3つの重要な過渡的な特殊ケースまたは例外があります。これらの過渡的な例外は、より多くのパッケージがモジュールにオプトインするにつれて、時間の経過とともに重要性が低下するでしょう。

3つの過渡的な例外

  1. gopkg.in

    gopkg.inで始まるインポートパス(gopkg.in/yaml.v1gopkg.in/yaml.v2など)を使用する既存のコードは、モジュールにオプトインした後も、その形式をモジュールパスとインポートパスに引き続き使用できます。

  2. 非モジュールv2+パッケージをインポートする際の「+incompatible」

    モジュールは、モジュール自体にオプトインしていないv2+パッケージをインポートできます。有効なv2+semverタグを持つ非モジュールv2+パッケージは、インポートするモジュールのgo.modファイルに+incompatibleサフィックス付きで記録されます。+incompatibleサフィックスは、v2+パッケージがv2.0.0のような有効なv2+semverタグを持っているにもかかわらず、そのv2+パッケージがモジュールに積極的にオプトインしておらず、したがってそのv2+パッケージはセマンティックインポートバージョニングの含意とインポートパスでのメジャーバージョンの使用方法を理解せずに作成されたと想定されることを示します。したがって、モジュールモードで操作する場合、goツールは非モジュールv2+パッケージをパッケージのv1バージョンシリーズの(互換性のない)拡張として扱い、そのパッケージはセマンティックインポートバージョニングを認識していないと想定します。そして、+incompatibleサフィックスは、goツールがそうしていることを示すものです。

  3. モジュールモードが無効な場合の「最小限のモジュール互換性」

    後方互換性を助けるため、Goバージョン1.9.7+、1.10.3+、および1.11は更新され、これらのリリースでビルドされたコードが、既存のコードの変更を必要とせずにv2+モジュールを適切に消費できるように容易になりました。この動作は「最小限のモジュール互換性」と呼ばれ、Go 1.11でGO111MODULE=offを設定している場合や、Goバージョン1.9.7+または1.10.3+を使用している場合など、goツールで完全なモジュールモードが無効な場合にのみ有効になります。Go 1.9.7+、1.10.3+、および1.11でこの「最小限のモジュール互換性」メカニズムに依存する場合、モジュールにオプトインしていないパッケージは、インポートされたv2+モジュールのインポートパスにメジャーバージョンを含めません。対照的に、モジュールにオプトインしているパッケージは、インポートパスにメジャーバージョンを含める必要がありますgoツールがセマンティックインポートバージョニングを完全に認識している完全なモジュールモードで動作しているときにv2+モジュールを適切にインポートするため)。

v2+モジュールをリリースするために必要な正確なメカニズムについては、以下の「モジュールをリリースする(v2以上)」セクションを参照してください。

モジュールの使用方法

モジュールサポートのインストールとアクティベート方法

モジュールを使用するには、2つのインストールオプションがあります。

インストール後、次の2つの方法のいずれかでモジュールサポートを有効にできます。

  • $GOPATH/srcツリーの外のディレクトリで、現在のディレクトリまたはその親ディレクトリに有効なgo.modファイルがあり、環境変数GO111MODULEが設定されていない(または明示的にautoに設定されている)状態でgoコマンドを呼び出す。
  • GO111MODULE=on環境変数を設定してgoコマンドを呼び出します。

モジュールの定義方法

既存のプロジェクトのgo.modを作成するには

  1. GOPATHの外部にあるモジュールのソースツリーのルートに移動します

    $ cd <project path outside $GOPATH/src>         # e.g., cd ~/projects/hello
    

    GOPATHの外では、モジュールモードをアクティブにするためにGO111MODULEを設定する必要はありません。

    または、GOPATH内で作業したい場合

    $ export GO111MODULE=on                         # manually active module mode
    $ cd $GOPATH/src/<project path>                 # e.g., cd $GOPATH/src/you/hello
    
  2. 最初のモジュール定義を作成し、それをgo.modファイルに書き込みます

    $ go mod init
    

    go mod initは、補助データ(VCSメタデータなど)を使用して適切なモジュールパスを自動的に決定できることが多いですが、go mod initがモジュールパスを自動的に決定できないと報告した場合、またはそのパスを上書きする必要がある場合は、go mod initのオプション引数としてモジュールパスを指定できます。例は次のとおりです。

    $ go mod init github.com/my/repo
    

    依存関係にv2以上のモジュールが含まれている場合、またはv2以上のモジュールを初期化している場合は、go mod initの実行後、上記の「意味的インポートバージョン管理」セクションで説明されているように、go.modおよび.goコードを編集して、インポートパスおよびモジュールパスに/vNを追加する必要がある場合があることに注意してください。これは、go mod initdepまたは他の依存関係マネージャーから依存関係情報を自動的に変換した場合でも適用されます。(このため、go mod initを実行した後、通常はgo build ./...などを正常に実行するまでgo mod tidyを実行すべきではありません。これはこのセクションに示されているシーケンスです)。

Go 1.21.13以前のバージョンで使用する場合、このステップでは、既存のdepGopkg.lockファイルまたはサポートされている他の9つの依存関係形式から必要な情報を変換し、既存の構成に一致するようにrequireステートメントを追加します。

  1. モジュールをビルドします。モジュールのルートディレクトリから実行すると、./...パターンは現在のモジュール内のすべてのパッケージに一致します。go buildは、この特定のビルド呼び出しのインポートを満たすために、必要に応じて不足している依存関係または未変換の依存関係を自動的に追加します。

    $ go build ./...
    
  2. 設定されたモジュールをテストし、選択されたバージョンで機能することを確認します。

    $ go test ./...
    
  3. (オプション)モジュールのテストと、すべての直接的および間接的な依存関係のテストを実行して、非互換性をチェックします

    $ go test all
    

リリースにタグ付けする前に、以下の「リリース準備の方法」セクションを参照してください。

これらのトピックすべてに関する詳細については、公式モジュールドキュメントの主要な入り口がgolang.orgで利用できます。

依存関係のアップグレードとダウングレード方法

依存関係の日常的なアップグレードとダウングレードは、「go get」を使用して行う必要があり、これによりgo.modファイルが自動的に更新されます。または、go.modを直接編集することもできます。

さらに、「go build」、「go test」、さらには「go list」などのgoコマンドは、インポートを満たすために必要に応じて新しい依存関係を自動的に追加します(go.modを更新し、新しい依存関係をダウンロードします)。

依存関係を最新バージョンにアップグレードするには

go get example.com/package

依存関係とそのすべての依存関係を最新バージョンにアップグレードするには

go get -u example.com/package

すべての直接的および間接的な依存関係で使用可能なマイナーおよびパッチのアップグレードを表示するには

go list -u -m all

直接的な依存関係のみで使用可能なマイナーおよびパッチのアップグレードを表示するには、次のコマンドを実行します。

go list -u -f '{{if (and (not (or .Main .Indirect)) .Update)}}{{.Path}}: {{.Version}} -> {{.Update.Version}}{{end}}' -m all 2> /dev/null

現在のモジュールのすべての直接的および間接的な依存関係を最新バージョンにアップグレードするには、モジュールルートディレクトリから次を実行します。

  • 最新のマイナーまたはパッチリリースを使用するためのgo get -u ./... (テスト依存関係もアップグレードするには-tを追加)
  • 最新のパッチリリースを使用するためのgo get -u=patch ./... (テスト依存関係もアップグレードするには-tを追加)

go get foofooを最新バージョンに更新します。go get foogo get foo@latestと同等です。つまり、@バージョンが指定されていない場合、@latestがデフォルトです。

このセクションで「最新」とは、semverタグが付いている最新バージョン、またはsemverタグがない場合は最新の既知のコミットを指します。プレリリース版タグは、リポジトリに他のsemverタグがない限り、「最新」として選択されません(詳細)。

よくある間違いは、go get -u fooが単にfooの最新バージョンを取得すると考えることです。実際には、go get -u fooまたはgo get -u foo@latest-uは、fooのすべての直接および間接的な依存関係も最新バージョンにすることを意味します。fooをアップグレードする際の一般的な出発点は、-uなしでgo get fooまたはgo get foo@latestを実行することです(そして、動作するようになったら、go get -u=patch foogo get -u=patchgo get -u foo、またはgo get -uを検討します)。

より具体的なバージョンにアップグレードまたはダウングレードするには、`go get`は、パッケージ引数に`@version`サフィックスまたは「モジュールクエリ」を追加することで、バージョン選択をオーバーライドできます。例えば、`go get foo@v1.6.2`、`go get foo@e3702bed2`、または`go get foo@'<v1.6.2'`です。

go get foo@master(Mercurialではfoo@default)のようにブランチ名を使用することは、semverタグがあるかどうかにかかわらず最新のコミットを取得する方法の1つです。

一般に、semverタグに解決されないモジュールクエリは、go.modファイルに擬似バージョンとして記録されます。

ここでのトピックに関する詳細については、goコマンドのドキュメントの「モジュール対応のgo get」および「モジュールクエリ」のセクションを参照してください。

モジュールは、まだモジュールにオプトインしていないパッケージを消費することができ、利用可能なsemverタグをgo.modに記録し、それらのsemverタグを使用してアップグレードまたはダウングレードすることができます。モジュールは、まだ適切なsemverタグを持たないパッケージも消費することができます(その場合、それらはgo.modに擬似バージョンを使用して記録されます)。

依存関係をアップグレードまたはダウングレードした後、非互換性をチェックするために、ビルド内のすべてのパッケージ(直接的および間接的な依存関係を含む)のテストを再実行したい場合があります。

$ go test all

リリース準備の方法

モジュールをリリースする(全バージョン)

モジュールのリリースを作成するためのベストプラクティスは、最初のモジュール実験の一環として出現すると予想されます。これらの多くは、将来的には「go release」ツールによって自動化される可能性があります。

リリースにタグ付けする前に考慮すべき現在の推奨されるベストプラクティスをいくつかご紹介します。

  • go mod tidyを実行して、余分な要件を削除し(ここで説明されているように)、現在のgo.modがすべての可能なビルドタグ/OS/アーキテクチャの組み合わせを反映していることを確認します(ここで説明されているように)。

    • 対照的に、go buildgo testのような他のコマンドは、もはや不要になった依存関係をgo.modから削除せず、現在のビルド呼び出しのタグ/OS/アーキテクチャに基づいてのみgo.modを更新します。
  • go test allを実行してモジュールをテストし(直接的および間接的な依存関係のテストも実行して)、現在選択されているパッケージバージョンが互換性があることを検証します。

    • 可能なバージョン組み合わせの数はモジュールの数に対して指数関数的に増加するため、一般的に、依存関係がすべての可能な依存関係の組み合わせに対してテストされていると期待することはできません。
    • モジュールの作業の一環として、go test allより役立つように再定義されました。現在のモジュール内のすべてのパッケージと、現在のモジュールでは関係ないパッケージを除いて、1つ以上のインポートのシーケンスを通じて依存するすべてのパッケージを含みます。
  • go.sumファイルがgo.modファイルと一緒にコミットされていることを確認してください。詳細と理論的根拠については、以下のFAQを参照してください。

モジュールをリリースする(v2以上)

v2以上のモジュールをリリースする場合は、まず上記の「セマンティックインポートバージョン管理」セクションの議論を確認してください。これには、v2+モジュールのモジュールパスとインポートパスにメジャーバージョンが含まれる理由、およびGoバージョン1.9.7+と1.10.3+がその移行を簡素化するためにどのように更新されたかが含まれています。

モジュールを導入する前にすでにv2.0.0以上のタグが付けられていた既存のリポジトリまたはパッケージセットに対してモジュールを初めて導入する場合、推奨されるベストプラクティスは、モジュールを初めて導入する際にメジャーバージョンをインクリメントすることであることに注意してください。たとえば、あなたがfooの作者で、fooリポジトリの最新タグがv2.2.2であり、fooがまだモジュールを導入していない場合、ベストプラクティスは、モジュールを導入するfooの最初のリリース(したがって、go.modファイルを含むfooの最初のリリース)にv3.0.0を使用することです。この場合、メジャーバージョンをインクリメントすることで、fooの利用者への明確性が向上し、必要に応じてfooのv2シリーズに非モジュールパッチまたはマイナーリリースを追加でき、import "foo"とそれに続くrequire foo v2.2.2+incompatible、またはimport "foo/v3"とそれに続くrequire foo/v3 v3.0.0を行った場合に異なるメジャーバージョンが生じることを、モジュールベースのfooの利用者に強く示します。(モジュールを初めて導入する際のメジャーバージョンをインクリメントするこのアドバイスは、最新バージョンがv0.x.xまたはv1.x.xである既存のリポジトリまたはパッケージには適用されないことに注意してください)。

v2以上のモジュールをリリースするには、2つの代替メカニズムがあります。どちらのテクニックでも、モジュール作成者が新しいタグをプッシュすると、新しいモジュールリリースが利用可能になることに注意してください。v3.0.0リリースを作成する例を使用して、2つのオプションは次のとおりです。

  1. メジャーブランチ: go.modファイルを更新し、moduleディレクティブのモジュールパスの末尾に/v3を含めます(例:module github.com/my/module/v3)。モジュール内のインポートステートメントも/v3を使用するように更新します(例:import "github.com/my/module/v3/mypkg")。リリースにv3.0.0とタグ付けします。

    • Goバージョン1.9.7+、1.10.3+、および1.11は、このアプローチを使用して作成されたv2+モジュールを、まだモジュールにオプトインしていないコンシューマーコードの更新を必要とせずに、適切に消費およびビルドできます(上記の「意味的インポートバージョン管理」セクションで説明されているとおり)。
    • コミュニティツールgithub.com/marwan-at-work/modが、この手順の自動化を支援しています。概要については、リポジトリまたはコミュニティツールFAQを参照してください。
    • このアプローチでの混乱を避けるため、モジュールのv3.*.*コミットを別のv3ブランチに置くことを検討してください。
    • 注意: 新しいブランチを作成することは必須ではありません。もし以前にmasterでリリースしていて、masterでv3.0.0をタグ付けしたいのであれば、それは実行可能な選択肢です。(ただし、masterで互換性のないAPI変更を導入すると、Go 1.11より前、またはGo 1.11+でモジュールモードが有効になっていない場合、goツールがsemverを認識しないため、go get -uを発行する非モジュールユーザーに問題を引き起こす可能性があることに注意してください)。
    • depなどの既存の依存関係管理ソリューションでは、この方法で作成されたv2+モジュールを消費する際に問題が発生する可能性があります。例として、dep#1962を参照してください。
  2. メジャーサブディレクトリ: 新しいv3サブディレクトリ(例: my/module/v3)を作成し、そのサブディレクトリに新しいgo.modファイルを配置します。モジュールパスは/v3で終わる必要があります。コードをv3サブディレクトリにコピーまたは移動します。モジュール内のインポートステートメントも/v3を使用するように更新します(例: import "github.com/my/module/v3/mypkg")。リリースにv3.0.0とタグ付けします。

    • これにより、後方互換性が向上します。特に、1.9.7および1.10.3より古いGoバージョンでも、このアプローチを使用して作成されたv2+モジュールを適切に消費およびビルドできます。
    • より洗練されたアプローチとしては、型エイリアス (Go 1.9で導入) と、異なるサブディレクトリに存在するメジャーバージョン間の転送シムを悪用することが考えられます。これにより、追加の互換性を提供し、あるメジャーバージョンを別のメジャーバージョンとして実装できますが、モジュール作者にとってはより多くの作業が必要になります。これを自動化する進行中のツールはgoforwardです。詳細と理論的根拠、およびgoforwardの動作する初期バージョンについては、こちらを参照してください。
    • depなどの既存の依存関係管理ソリューションは、この方法で作成されたv2+モジュールを消費できるはずです。

これらの代替案に関するより詳細な議論については、https://research.swtch.com/vgo-moduleを参照してください。

リリースを公開する

新しいモジュールバージョンは、モジュールソースコードを含むリポジトリにタグをプッシュすることで公開できます。タグは、プレフィックスバージョンの2つの文字列を連結して形成されます。

バージョンは、リリースのセマンティックインポートバージョンです。セマンティックインポートバージョン管理のルールに従って選択する必要があります。

プレフィックスは、リポジトリ内でモジュールがどこに定義されているかを示します。モジュールがリポジトリのルートに定義されている場合、プレフィックスは空であり、タグはバージョンのみです。しかし、マルチモジュールリポジトリでは、プレフィックスが異なるモジュールのバージョンを区別します。プレフィックスは、モジュールが定義されているリポジトリ内のディレクトリです。リポジトリが上記で説明したメジャーサブディレクトリパターンに従う場合、プレフィックスにはメジャーバージョンのサフィックスは含まれません。

例えば、モジュールexample.com/repo/sub/v2があり、バージョンv2.1.6を公開したいとします。リポジトリルートはexample.com/repoに対応し、モジュールはリポジトリ内のsub/v2/go.modで定義されています。このモジュールのプレフィックスはsub/です。このリリースの完全なタグはsub/v2.1.6となるはずです。

モジュールへの移行

このセクションでは、モジュールへの移行時に行う主要な決定を簡潔に列挙し、その他の移行関連トピックもリストアップしています。詳細については、一般的に他のセクションが参照されます。

この資料は、主にモジュール実験の一環としてコミュニティから生まれたベストプラクティスに基づいています。したがって、これは進行中のセクションであり、コミュニティの経験が増えるにつれて改善される予定です。

要約

  • モジュールシステムは、全体のGoエコシステム内の異なるパッケージが異なる速度でオプトインできるように設計されています。
  • すでにv2以上のバージョンにあるパッケージには、主にセマンティックインポートバージョン管理の影響により、より多くの移行の考慮事項があります。
  • 新しいパッケージおよびv0またはv1のパッケージは、モジュールを採用する際の考慮事項が大幅に少なくなります。
  • Go 1.11で定義されたモジュールは、古いGoバージョンでも使用できます(ただし、正確なGoバージョンは、以下に概説するように、メインモジュールとその依存関係が使用する戦略に依存します)。

移行のトピック

以前の依存関係マネージャーからの自動移行

  • Go 1.21.13以前のバージョンでは、go mod initは、dep、glide、govendor、godep、およびその他5つの既存の依存関係マネージャーから必要な情報を自動的に変換し、同等のビルドを生成するgo.modファイルを作成します。
  • v2+モジュールを作成している場合は、変換されたgo.modmoduleディレクティブに適切な/vN(例:module foo/v3)が含まれていることを確認してください。
  • v2+モジュールをインポートしている場合、以前の依存関係マネージャーから変換された後にgo mod initが生成するrequireステートメントに/vNを追加するために、初期変換後に手動で調整する必要がある場合があることに注意してください。詳細については、上記の「モジュールの定義方法」セクションを参照してください。
  • さらに、go mod initは、必要な/vNをインポートステートメントに追加するために.goコードを編集しません。変換を自動化するためのコミュニティツールのオプションを含む、必要な手順については、上記の「セマンティックインポートバージョン管理」および「モジュールをリリースする(v2以上)」セクションを参照してください。

Goの古いバージョンと非モジュール利用者への依存情報提供

  • 古いバージョンのGoは、go mod vendorによって作成されたvendorディレクトリを消費する方法を理解しています。Go 1.11および1.12+も、モジュールモードが無効な場合に同様に動作します。したがって、ベンダーは、モジュールを完全に理解しないGoの古いバージョンや、モジュールを有効にしていない利用者に対して、モジュールが依存関係を提供する1つの方法です。詳細については、ベンダーFAQgoコマンドのドキュメントを参照してください。

既存のインストール手順を更新する

  • モジュール導入以前は、インストール手順にgo get -u fooを含めるのが一般的でした。モジュールfooを公開している場合は、モジュールベースの利用者向けの説明で-uを削除することを検討してください。
    • -uは、goツールにfooのすべての直接および間接的な依存関係をアップグレードするように要求します。
    • モジュールの利用者は後でgo get -u fooを実行することを選択するかもしれませんが、最初のインストール手順に-uを含めない方が「高忠実度ビルド」のメリットが大きくなります。詳細については、「依存関係のアップグレードとダウングレード方法」を参照してください。
    • go get -u fooはまだ動作し、インストール手順として有効な選択肢となり得ます。
  • さらに、go get fooはモジュールベースの利用者にとって厳密には必要ありません。
    • 単にimport "foo"というインポートステートメントを追加するだけで十分です。(go buildgo testのような後続のコマンドは、必要に応じて自動的にfooをダウンロードし、go.modを更新します)。
  • モジュールベースの利用者は、デフォルトではvendorディレクトリを使用しません。
    • goツールでモジュールモードが有効な場合、モジュールを消費する際にvendorは厳密には必須ではありません(go.modに含まれる情報とgo.sumの暗号化されたチェックサムを考慮すると)。しかし、既存のインストール手順の中には、goツールがデフォルトでvendorを使用すると仮定しているものもあります。詳細については、ベンダーFAQを参照してください。
  • go get foo/...を含むインストール手順は、場合によっては問題を引き起こす可能性があります(#27215の議論を参照)。

既存のインポートパスを壊さないようにする

モジュールは、module github.com/my/moduleのようなmoduleディレクティブを介してgo.modでそのIDを宣言します。モジュール内のすべてのパッケージは、モジュールに対応する宣言されたモジュールパスと一致するインポートパス(ルートパッケージの場合は完全に一致するか、インポートパスのプレフィックスとしてモジュールパスを持つ)を持つモジュール対応の利用者によってインポートされなければなりません。インポートパスと対応するモジュールの宣言されたモジュールパスとの間に不一致がある場合、goコマンドはunexpected module pathエラーを報告します。

既存のパッケージセットに対してモジュールを採用する場合、モジュールを採用する際にメジャーバージョンをインクリメントしない限り、既存の利用者が使用している既存のインポートパスを壊さないように注意する必要があります。

たとえば、既存のREADMEが利用者にimport "gopkg.in/foo.v1"を使用するように指示しており、v1リリースでモジュールを採用した場合、最初のgo.modはほぼ確実にmodule gopkg.in/foo.v1と読むべきです。gopkg.inの使用から離れたい場合、それは現在の利用者にとって破壊的な変更となるでしょう。1つのアプローチは、後でv2に移行する場合にmodule github.com/repo/foo/v2のようなものに変更することです。

モジュールパスとインポートパスは大文字と小文字を区別することに注意してください。たとえば、モジュールをgithub.com/Sirupsen/logrusからgithub.com/sirupsen/logrusに変更することは、GitHubが自動的に一方のリポジトリ名を新しいリポジトリ名に転送する場合でも、利用者にとって破壊的な変更です。

モジュールを採用した後、go.modでモジュールパスを変更することは、破壊的な変更です。

全体として、これは「インポートプラグマ」または「インポートパス強制」とも呼ばれる「インポートパスチェック」による、モジュール導入以前の正規のインポートパスの強制に似ています。たとえば、パッケージgo.uber.org/zapは現在github.com/uber-go/zapでホストされていますが、誤ったGitHubベースのインポートパスを使用するモジュール導入以前の利用者に対してエラーをトリガーする、パッケージ宣言の隣にインポートパスコメントを使用しています。

package zap // import "go.uber.org/zap"

インポートパスコメントは、go.modファイルのモジュールステートメントによって廃止されました。

v2以上のパッケージでモジュールを初めて導入する際のメジャーバージョンのインクリメント

  • モジュールを採用する前に、すでにv2.0.0以上のタグが付けられていたパッケージがある場合、推奨されるベストプラクティスは、モジュールを初めて採用する際にメジャーバージョンをインクリメントすることです。たとえば、v2.0.1で、まだモジュールを採用していない場合、モジュールを採用する最初のリリースにはv3.0.0を使用します。詳細については、上記の「モジュールをリリースする(v2以上)」セクションを参照してください。

v2+モジュールは、単一のビルド内で複数のメジャーバージョンを許可します。

  • モジュールがv2以上である場合、複数のメジャーバージョンが単一のビルドに存在できる(例: foofoo/v3が単一のビルドになる可能性がある)ことを意味します。
    • これは「インポートパスが異なるパッケージは異なるパッケージである」というルールから自然に導かれます。
    • この現象が発生すると、パッケージレベルの状態(例:fooのパッケージレベルの状態とfoo/v3のパッケージレベルの状態)の複数のコピーが存在し、各メジャーバージョンは独自のinit関数を実行します。
    • このアプローチは、ダイヤモンド依存関係の問題、大規模なコードベース内での新しいバージョンへの段階的な移行、およびあるメジャーバージョンを別のメジャーバージョンをラップするシムとして実装することを可能にするなど、モジュールシステムの複数の側面を助けます。
  • 関連する議論については、https://research.swtch.com/vgo-importの「Singleton Problemsの回避」セクションまたは#27514を参照してください。

非モジュールコードを消費するモジュール

  • モジュールは、まだモジュールにオプトインしていないパッケージを消費することができ、適切なパッケージバージョン情報はインポートするモジュールのgo.modに記録されます。モジュールは、まだ適切なsemverタグを持たないパッケージも消費できます。詳細については、以下のFAQ参照
  • モジュールは、モジュールにオプトインしていないv2+パッケージもインポートできます。インポートされたv2+パッケージが有効なsemverタグを持つ場合、+incompatibleサフィックス付きで記録されます。詳細については、以下のFAQ参照

モジュールを消費する非モジュールコード

  • v0およびv1モジュールを消費する非モジュールコード:

    • モジュールにまだオプトインしていないコードは、v0およびv1モジュールを消費してビルドできます(使用されるGoバージョンに関連する要件はありません)。
  • v2+モジュールを消費する非モジュールコード:

既存のv2+パッケージ作成者向け戦略

モジュールへのオプトインを検討している既存のv2+パッケージの作者にとって、代替アプローチを要約する方法の1つは、3つのトップレベル戦略から選択することです。各選択には、それに続く決定とバリエーションがあります(上記で概説したとおり)。これらの代替トップレベル戦略は次のとおりです。

  1. クライアントにGoバージョン1.9.7+、1.10.3+、または1.11+の使用を要求する.

    このアプローチは「メジャーブランチ」アプローチを使用し、1.9.7および1.10.3にバックポートされた「最小限のモジュール認識」に依存します。詳細については、上記の「意味的インポートバージョン管理」および「モジュールをリリースする(v2以上)」セクションを参照してください。

  2. クライアントがGo 1.8のような古いGoバージョンを使用できるようにする.

    このアプローチは「メジャーサブディレクトリ」アプローチを使用し、/v2/v3のようなサブディレクトリを作成します。詳細については、上記の「セマンティックインポートバージョン管理」および「モジュールをリリースする(v2以上)」セクションを参照してください。

  3. モジュールへのオプトインを待つ.

    この戦略では、モジュールにオプトインしているクライアントコードと、モジュールにオプトインしていないクライアントコードの両方で引き続き機能します。時間が経つにつれて、Goバージョン1.9.7+、1.10.3+、および1.11+がより長い期間利用可能になり、将来的にはGoバージョン1.9.7+/1.10.3+/1.11+を要求するのがより自然またはクライアントにとって友好的になり、その時点で上記の戦略1(Goバージョン1.9.7+、1.10.3+、または1.11+を要求する)または上記の戦略2(ただし、Go 1.8のような古いGoバージョンをサポートするために最終的に上記の戦略2を選択する予定がある場合は、それは今すぐ行うことができます)を実装できます。

追加リソース

ドキュメントと提案

  • 公式ドキュメント
    • golang.org上のモジュールに関する最新のHTMLドキュメント
    • モジュールの詳細については、go help modulesを実行してください。(これはgo helpを介したモジュールの主要な入り口です)
    • go modコマンドの詳細については、go help modを実行してください。
    • モジュール対応モードでのgo getの動作の詳細については、go help module-getを実行してください。
    • file:/// URLを介した純粋なファイルベースのオプションを含む、モジュールプロキシの詳細については、go help goproxyを実行してください。
  • Russ Coxによるvgoに関する一連の最初のブログ記事「Go & Versioning」(2018年2月20日初投稿)
  • 提案を紹介する公式golang.orgブログ記事(2018年3月26日)
    • これは、完全なvgoブログシリーズよりも簡潔な提案の概要を提供し、提案の背景にある歴史とプロセスの一部も示しています。
  • 公式Versioned Go Modules Proposal(最終更新日 2018年3月20日)

入門資料

追加資料

初期のVgo提案以降の変更点

提案、プロトタイプ、およびベータプロセスの一環として、コミュニティ全体で400以上の課題が作成されました。引き続きフィードバックをお願いいたします。

主にコミュニティからのフィードバックに基づいて行われた、より大きな変更と改善の一部を以下に示します。

  • トップレベルのベンダーサポートは維持され、vgoベースのビルドがベンダーディレクトリを完全に無視するのではなく保持されました(議論CL)。
  • Goの古いバージョン1.9.7+および1.10.3+がv2+プロジェクトのモジュールをより簡単に消費できるように、最小限のモジュール認識をバックポートしました(議論CL)。
  • 既存のパッケージでまだgo.modがない場合でも、vgoがデフォルトでv2+タグを使用することを許可しました(関連する動作の最近の更新はここで説明されています)。
  • go get -u=patchコマンドによるサポートを追加し、すべての推移的依存関係を同じマイナーバージョンの最新のパッチレベルバージョンに更新できるようにしました(議論ドキュメント)。
  • 環境変数による追加制御(例: #26585のGOFLAGS、CL)。
  • go.modの更新を許可するかどうか、ベンダーディレクトリの使用方法、ネットワークアクセスを許可するかどうかについて、よりきめ細かい制御を追加しました(例: -mod=readonly、-mod=vendor、GOPROXY=off; 最近の変更に関するCL)。
  • より柔軟なreplaceディレクティブを追加しました(CL)。
  • モジュールを照会する追加の方法を追加しました(人間による利用のためだけでなく、エディター/IDEの統合を改善するため)。
  • これまでの経験に基づいて、go CLIのUXは引き続き改良されています(例: #26581CL)。
  • CIやDockerビルドなどのユースケース向けに、go mod downloadによるキャッシュのウォームアップの追加サポート(#26610)。
  • 最も可能性が高い: GOBINに特定のバージョンのプログラムをインストールするためのより良いサポート(#24250)。

GitHubの課題

よくある質問

バージョンはどのように互換性がないとマークされますか?

requireディレクティブは、任意のモジュールが依存関係Dのバージョン>= x.y.zでビルドされるべきであることを宣言できるようにします(これは、モジュールDのバージョン< x.y.zとの非互換性のために指定される場合があります)。経験的データによると、これはdepcargoで使われる主要な制約形式です。さらに、ビルド内のトップレベルモジュールは、特定のバージョンの依存関係をexcludeしたり、他のモジュールを異なるコードでreplaceしたりできます。詳細と根拠については、完全な提案書を参照してくださいこちら

バージョン管理されたモジュールの提案の主要な目標の1つは、Goコードのバージョンに関して、ツールと開発者の両方にとって共通の語彙とセマンティクスを追加することです。これにより、将来的に追加の形式の非互換性を宣言する機能の基盤が築かれます。たとえば、次のような可能性があります。

  • 初期のvgoブログシリーズで説明されているように、非推奨バージョンを宣言する。
  • 提案プロセス中に、例えばここで議論されたように、外部システムでのモジュール間のペアワイズ非互換性を宣言すること。
  • リリース公開後に、モジュールのペアワイズ互換性のないバージョンまたは安全でないバージョンを宣言します。たとえば、#24031#26829で進行中の議論を参照してください。

いつ古い挙動と新しいモジュールベースの挙動が得られますか?

一般的に、モジュールはGo 1.11でオプトインであるため、設計上、古い動作がデフォルトで維持されます。

古い1.10の現状動作と、新しいオプトインモジュールベースの動作をいつ取得するかを要約すると次のとおりです。

  • GOPATH内 — 古い1.10の動作がデフォルト(モジュールは無視されます)
  • GOPATHの外部で、go.modのあるファイルツリー内 — モジュールの動作がデフォルト
  • GO111MODULE 環境変数
    • 未設定またはauto — 上記のデフォルトの動作
    • on — ディレクトリの場所に関わらずモジュールサポートを強制的に有効にする
    • off — ディレクトリの場所に関わらずモジュールサポートを強制的に無効にする

なぜgo getによるツールのインストールがcannot find main moduleエラーで失敗するのですか?

これは、GO111MODULE=onを設定しているにもかかわらず、go getを実行する際にgo.modファイルを持つファイルツリー内にいない場合に発生します。

最も簡単な解決策は、GO111MODULEを未設定のままにする(または同等に明示的にGO111MODULE=autoに設定する)ことで、このエラーを回避できます。

モジュールが存在する主な理由の1つは、正確な依存関係情報を記録することであることを思い出してください。この依存関係情報は現在のgo.modに書き込まれます。go.modを持つファイルツリー内にいないが、GO111MODULE=onを設定してgo getコマンドをモジュールモードで操作するように指示している場合、依存関係情報を記録するgo.modがないため、go getを実行するとcannot find main moduleというエラーが発生します。

解決策の選択肢は次のとおりです。

  1. GO111MODULEを未設定のままにする(デフォルト、または明示的にGO111MODULE=autoに設定する)と、より友好的な動作になります。これにより、モジュールの外部にいる場合にGo 1.10の動作が得られ、go getcannot find main moduleを報告することを回避できます。

  2. GO111MODULE=onのままにし、必要に応じてgo get中にモジュールを一時的に無効にし、Go 1.10の動作を有効にします。たとえば、GO111MODULE=off go get example.com/cmdを使用します。これは、alias oldget='GO111MODULE=off go get'のような簡単なスクリプトやシェルエイリアスに変換できます。

  3. 一時的なgo.modファイルを作成し、その後破棄します。これは@rogpeppeによる簡単なシェルスクリプトによって自動化されています。このスクリプトでは、バージョン情報をオプションでvgoget example.com/cmd[@version]を介して提供できます。(これは、エラーcannot use path@version syntax in GOPATH modeを回避するための解決策となりえます)。

  4. gobinは、メインパッケージをインストールして実行するためのモジュール対応コマンドです。デフォルトでは、gobinは最初に手動でモジュールを作成する必要なくメインパッケージをインストール/実行しますが、-mフラグを使用すると、既存のモジュールを使用して依存関係を解決するように指示できます。詳細と追加の使用例については、gobinREADMEおよびFAQを参照してください。

  5. ~/global-tools/go.modなど、グローバルにインストールされているツールを追跡するために使用するgo.modを作成し、グローバルにインストールされているツールに対してgo getまたはgo installを実行する前に、そのディレクトリにcdします。

  6. ~/tools/gorename/go.mod~/tools/goimports/go.modのように、各ツールを個別のディレクトリにgo.modを作成し、そのツールに対してgo getまたはgo installを実行する前に、適切なディレクトリにcdします。

この現在の制限は解決されるでしょう。しかし、主な問題は、モジュールが現在オプトインであり、完全な解決策はGO111MODULE=onがデフォルトの動作になるまで待たれる可能性が高いということです。これに関するより詳細な議論については、#24250を参照してください。これには次のコメントが含まれています。

これは最終的には確実に機能しなければなりません。私が確信できないのは、バージョンに関してこれが正確に何をするのかということです。一時的なモジュールルートとgo.modを作成し、インストールを行い、それからそれを破棄するのでしょうか?おそらくそうでしょう。しかし、私は完全に確信していませんし、今のところ、vgoにgo.modツリー外の作業をさせることで人々を混乱させたくありませんでした。確かに、最終的なgoコマンド統合はこれをサポートする必要があります。

このFAQでは、グローバルにインストールされたツールの追跡について議論してきました。

代わりに、特定のモジュールに必要なツールを追跡したい場合は、次のFAQを参照してください。

モジュールのツール依存関係をどのように追跡できますか?

もしあなたが、

  • モジュールで作業中にGoベースのツール(例:stringer)を使用したい場合、そして
  • そのツールのバージョンをモジュールのgo.modファイルで追跡しながら、全員がそのツールの同じバージョンを使用するようにしたい場合

Go 1.24以降では、go.modにtoolディレクティブを追加できます。

go 1.24

...

tool golang.org/x/tools/cmd/stringer

Go 1.24より前は、ツールのインポートステートメント(import _ "golang.org/x/tools/cmd/stringer"など)と//go:build toolsビルド制約を含むtools.goファイルをモジュールに追加する方法が推奨されていました。インポートステートメントは、goコマンドがツールのバージョン情報をモジュールのgo.modに正確に記録することを可能にし、//go:build toolsビルド制約は、通常のビルドが実際にツールをインポートするのを防ぎます。

具体的な例については、この「Go Modules by Example」のウォークスルーを参照してください。

(Go 1.16以降では)go install tool@versionを使用して特定のバージョンをインストールしたり、(Go 1.17以降では)go run tool@versionを使用してインストールせずにツールを実行したりすることもできます。これは#42088#40276で実装されており、tools.goが不要になります。

IDE、エディター、およびgoimports、gorenameなどの標準ツールにおけるモジュールサポートの状況はどうなっていますか?

エディターやIDEでのモジュールサポートは開始されています。

  • GoLand: こちらに記載されているように、GOPATH内外でのモジュールの完全なサポートを現在提供しています。これには、補完、構文解析、リファクタリング、ナビゲーションが含まれます。
  • VS Code: 作業は完了し、MSはGOPATHよりもモジュールを推奨しており、以前の追跡課題(#1532)はクローズされました。ドキュメントはVS Codeモジュールリポジトリで入手できます。
  • Atom (go-plus使用): 追跡課題は#761です。
  • vim (vim-go使用): go.modの構文ハイライトとフォーマットの初期サポートが実装されました。より広範なサポートは#1906で追跡されています。
  • emacs (go-mode.el使用): 追跡課題は#237です。

goimports、guru、gorenameなどの他のツールの状況は、包括的な課題#24661で追跡されています。最新の状況については、その包括的な課題を参照してください。

特定のツールの追跡課題には以下が含まれます。

  • gocode: 追跡課題はmdempsky/gocode/#46です。nsf/gocodeは、nsf/gocodeからmdempsky/gocodeへの移行を推奨しています。
  • go-tools (staticcheck、megacheck、gosimpleなどのdominikhによるツール): サンプルの追跡課題はdominikh/go-tools#328です。

一般に、エディター、IDE、その他のツールがまだモジュール対応になっていない場合でも、GOPATH内でモジュールを使用し、go mod vendorを実行すれば(適切な依存関係がGOPATH経由で取得されるため)、その機能の多くはモジュールで動作するはずです。

完全な解決策は、パッケージをロードするプログラムをgo/buildからgolang.org/x/tools/go/packagesに移行することです。このパッケージはモジュールを認識した方法でパッケージを見つける方法を理解しています。これは最終的にgo/packagesになるでしょう。

FAQ — 追加制御

モジュールを扱うためにどのようなコミュニティツールがありますか?

コミュニティはモジュール上にツールを構築し始めています。例えば

  • github.com/rogpeppe/gohack
    • replaceとマルチモジュールワークフローを自動化し、大幅に簡素化する新しいコミュニティツール。依存関係の1つを簡単に変更することもできます。
    • 例えば、gohack example.com/some/dependencyは、適切なリポジトリを自動的にクローンし、必要なreplaceディレクティブをgo.modに追加します。
    • gohack undoで、すべてのgohack replaceステートメントを削除します。
    • このプロジェクトは、他のモジュール関連のワークフローをより簡単にすることを目指して拡大を続けています。
  • github.com/marwan-at-work/mod
    • モジュールのメジャーバージョンを自動的にアップグレード/ダウングレードするためのコマンドラインツール。
    • go.modファイルとGoソースコード内の関連するインポートステートメントを自動的に調整します。
    • アップグレード時、またはv2以上のパッケージでモジュールを初めて導入する際に役立ちます。
  • github.com/akyoto/mgit
    • すべてのローカルプロジェクトのSemverタグを表示および制御できます。
    • タグ付けされていないコミットを表示し、それらをすべて一度にタグ付けできます(mgit -tag +0.0.1)。
  • github.com/goware/modvendor
    • シェルスクリプト、.cppファイル、.protoファイルなど、追加のファイルをvendor/フォルダーにコピーするのに役立ちます。
  • github.com/psampaz/go-mod-outdated
    • 古くなった依存関係を人間にわかりやすい方法で表示します。
    • 間接的な依存関係や更新のない依存関係をフィルタリングする方法を提供します。
    • 古くなった依存関係がある場合にCIパイプラインを中断する方法を提供します。
  • github.com/oligot/go-mod-upgrade
    • 古くなったGoの依存関係を対話的に更新します。

replaceディレクティブはいつ使用すべきですか?

上記の「go.mod」コンセプトセクションで説明されているように、replaceディレクティブは、トップレベルのgo.modでGoソースまたはgo.modファイルで見つかった依存関係を満たすために実際に何が使用されるかについて追加の制御を提供しますが、メインモジュール以外のモジュールのreplaceディレクティブはメインモジュールのビルド時に無視されます。

replaceディレクティブを使用すると、VCS(GitHubなど)にある別のモジュール、または相対パスまたは絶対ファイルパスを使用してローカルファイルシステムにある別のインポートパスを指定できます。replaceディレクティブからの新しいインポートパスは、実際のソースコードのインポートパスを更新することなく使用されます。

replaceは、依存関係に使用される正確なバージョンをトップレベルモジュールが制御できるようにします。例えば、

  • replace example.com/some/dependency => example.com/some/dependency v1.2.3

replaceは、フォークされた依存関係の使用も許可します。例えば、

  • replace example.com/some/dependency => example.com/some/dependency-fork v1.2.3

ブランチを参照することもできます。例えば、

  • replace example.com/some/dependency => example.com/some/dependency-fork master

例えば、依存関係の何かを修正または調査する必要がある場合、ローカルフォークを持ち、トップレベルのgo.modに次のようなものを追加できます。

  • replace example.com/original/import/path => /your/forked/import/path

replaceは、マルチモジュールプロジェクトでのモジュールのディスク上の相対パスまたは絶対パスをGoツールに通知するためにも使用できます。例えば、

  • replace example.com/project/foo => ../foo

: replaceディレクティブの右側がファイルシステムパスの場合、そのターゲットにはその場所にgo.modファイルが必要です。go.modファイルが存在しない場合は、go mod initで作成できます。

一般的に、replaceディレクティブの=>の左側にバージョンを指定するオプションがありますが、通常はそれを省略する方が変更に影響されにくいです(例えば、上記のすべてのreplaceの例でそうされているように)。

直接的な依存関係の各replaceディレクティブには、requireディレクティブが必要です。ファイルシステムパスから依存関係を置き換える場合、対応するrequireディレクティブのバージョンは本質的に無視されます。この場合、疑似バージョンv0.0.0はこれを明確にするための良い選択です。例えば、require example.com/module v0.0.0

go list -m allを実行することで、期待されるバージョンが取得されていることを確認できます。これは、replaceステートメントを考慮してビルドで使用される実際の最終バージョンを示します。

詳細については、「go mod edit」ドキュメントを参照してください。

github.com/rogpeppe/gohackは、これらのタイプのワークフローをはるかに簡単にします。特に、モジュールの依存関係の変更可能なチェックアウトを持つことが目標である場合です。概要については、リポジトリまたは直前のFAQを参照してください。

VCSから完全に離れてreplaceを使用する方法の詳細については、次のFAQを参照してください。

ローカルファイルシステム上でVCSを完全に使わずに作業できますか?

はい。VCSは必須ではありません。

VCSの外で一度に編集したい単一のモジュールがある場合(そして、合計で1つのモジュールしかないか、他のモジュールがVCSに存在する場合)、これは非常にシンプルです。この場合、単一のgo.modを含むファイルツリーを便利な場所に配置できます。単一のモジュールがVCSの外にある場合でも(go.modreplaceを使用する必要なく)、go buildgo testなどのコマンドは機能します。

ローカルディスク上で複数の相互に関連するモジュールを同時に編集したい場合、replaceディレクティブが1つのアプローチです。これは、replaceを使用してhelloモジュールをgoodbyeモジュールのディスク上の場所に(VCSに依存せずに)相対パスで指すgo.modのサンプルです。

module example.com/me/hello

require (
  example.com/me/goodbye v0.0.0
)

replace example.com/me/goodbye => ../goodbye

このスレッドで、小さな実行可能な例が示されています。

モジュールでベンダーを使用するにはどうすればよいですか?ベンダーは廃止されますか?

vgoに関する一連の初期のブログ投稿では、ベンダーリングを完全に廃止することを提案していましたが、コミュニティからのフィードバックにより、ベンダーリングのサポートが維持されました。

簡単に言えば、モジュールでベンダーリングを使用するには

  • go mod vendorは、メインモジュールのベンダーディレクトリをリセットして、go.modファイルとGoソースコードの状態に基づいてモジュールのすべてのパッケージをビルドおよびテストするために必要なすべてのパッケージを含めます。
  • デフォルトでは、go buildなどのgoコマンドは、モジュールモードの場合、ベンダーディレクトリを無視します。
  • -mod=vendorフラグ (例: go build -mod=vendor) は、goコマンドに対し、メインモジュールのトップレベルのベンダーディレクトリを使用して依存関係を満たすように指示します。このモードのgoコマンドは、go.mod内の依存関係の記述を無視し、ベンダーディレクトリが依存関係の正しいコピーを保持していると仮定します。メインモジュールのトップレベルのベンダーディレクトリのみが使用され、他の場所にあるベンダーディレクトリは引き続き無視されることに注意してください。
  • 一部の人々は、GOFLAGS=-mod=vendor環境変数を設定することで、ベンダーリングを日常的に利用したいと考えるでしょう。

Go 1.10のような古いバージョンのGoは、go mod vendorによって作成されたベンダーディレクトリを消費する方法を理解しています。Go 1.11および1.12以降も、モジュールモードが無効になっている場合は同様です。したがって、ベンダーリングは、モジュールを完全に理解しない古いバージョンのGo、およびモジュールを有効にしていないコンシューマーに依存関係を提供する1つの方法です。

ベンダーリングの使用を検討している場合は、ティップドキュメントの「モジュールとベンダーリング」および「依存関係のベンダーコピーの作成」セクションを読む価値があります。

「常時稼働」モジュールリポジトリとエンタープライズプロキシはありますか?

公開ホストされた「常時稼働」の不変モジュールリポジトリと、オプションでプライベートホストされたプロキシおよびリポジトリが利用可能になりつつあります。

  • proxy.golang.org - 公式プロジェクト - Googleが運営 - Goチームによって構築されたデフォルトのGoモジュールプロキシ。
  • proxy.golang.com.cn - 中国プロキシプロジェクト - China Golang Contributor Clubが運営 - 中国Goモジュールプロキシ。
  • mirrors.tencent.com/go - 商用プロジェクト - Tencent Cloudが運営 - Goモジュールプロキシの代替。
  • mirrors.aliyun.com/goproxy - 商用プロジェクト - Alibaba Cloudが運営 - Goモジュールプロキシの代替。
  • goproxy.cn - オープンソースプロジェクト - Qiniu Cloudが運営 - 中国で最も信頼されているGoモジュールプロキシ。
  • goproxy.io - オープンソースプロジェクト - China Golang Contributor Clubが運営 - Goモジュールのグローバルプロキシ。
  • Athens - オープンソースプロジェクト - セルフホスト型 - Goモジュールデータストアおよびプロキシ。
  • Goproxy - オープンソースプロジェクト - セルフホスト型 - 最小限のGoモジュールプロキシハンドラー。
  • THUMBAI - オープンソースプロジェクト - セルフホスト型 - Go modプロキシサーバーおよびGoバニティインポートパスサーバー。

プロキシを実行する必要はないことに注意してください。むしろ、1.11のgoツールは、より多くのエンタープライズユースケース(より高度な制御など)を可能にするために、および「GitHubがダウンした」または人々がGitHubリポジトリを削除したなどの状況をより良く処理するために、GOPROXYを介したオプションのプロキシサポートを追加しました。

go.modが更新されるタイミングや、goツールが依存関係を満たすためにネットワークを使用するタイミングを制御できますか?

デフォルトでは、go buildのようなコマンドは、インポートを満たすために必要に応じてネットワークにアクセスします。

一部のチームは、特定の時点でgoツールがネットワークにアクセスすることを禁止したり、goツールがgo.modを更新するタイミング、依存関係の取得方法、ベンダーの使用方法に関してより高度な制御を望んだりするでしょう。

goツールは、-mod=readonly-mod=vendorGOFLAGSGOPROXY=offGOPROXY=file:///filesystem/pathgo mod vendorgo mod downloadなど、これらのデフォルトの動作を調整または無効にするためのかなりの柔軟性を提供します。

これらのオプションに関する詳細は、公式ドキュメント全体に散らばっています。これらの動作に関連するノブの統合された概要を試みたコミュニティの試みの1つはこちらにあり、詳細については公式ドキュメントへのリンクも含まれています。

TravisやCircleCIなどのCIシステムでモジュールを使用するにはどうすればよいですか?

最も簡単な方法は、環境変数GO111MODULE=onを設定することでしょう。これはほとんどのCIシステムで機能するはずです。

しかし、一部のユーザーはまだモジュールを有効にしていないため、Go 1.11でモジュールを有効にした場合と無効にした場合の両方でCIでテストを実行することは価値があります。ベンダーリングも考慮すべきトピックです。

次の2つのブログ投稿で、これらのトピックについてより具体的に説明されています。

特定のパッケージやテストをビルドするために必要なモジュールをダウンロードするにはどうすればよいですか?

go mod downloadコマンド(または同等にgo mod download all)は、ビルドリスト内のすべてのモジュールをダウンロードします(go list -m allによって報告されるように)。これらのモジュールの多くはメインモジュール内のパッケージをビルドするために必要ありません。なぜなら、完全なビルドリストには、他のモジュールのテスト依存関係やツール依存関係などが含まれているからです。したがって、go mod downloadで作成されたDockerイメージは、必要以上に大きくなる可能性があります。

代わりに、go listの使用を検討してください。例えば、go list ./...は、パッケージ./...をビルドするために必要なモジュールをダウンロードします(モジュールルートディレクトリから実行された場合、メインモジュール内のパッケージのセット)。

テストの依存関係もダウンロードするには、go list -test ./...を使用します。

デフォルトでは、go listは現在のプラットフォームに必要な依存関係のみを考慮します。GOOSおよびGOARCHを設定して、go listに別のプラットフォームを考慮させることができます。例えば、GOOS=linux GOARCH=amd64 go list ./...-tagsフラグを使用して、特定のビルドタグを持つパッケージを選択することもできます。

この手法は、遅延モジュール読み込みが実装された場合(#36460参照)には、将来的に必要性が低くなる可能性があります。なぜなら、モジュールパターンallには含まれるモジュールの数が少なくなるからです。

FAQ — go.modとgo.sum

「go mod tidy」が間接的およびテストの依存関係を「go.mod」に記録するのはなぜですか?

モジュールシステムは、go.modに正確な依存関係の要件を記録します。(詳細については、上記のgo.modの概念セクション、またはgo.modのヒントドキュメントを参照してください)。

go mod tidyは、モジュールのテストに必要な依存関係を含めるように現在のgo.modを更新します。テストが失敗した場合、その失敗を再現するためにどの依存関係が使用されたかを知る必要があります。

go mod tidyは、現在のgo.modがOS、アーキテクチャ、ビルドタグのすべての可能な組み合わせに対する依存関係要件を反映していることを保証します(こちらに記載されているように)。対照的に、go buildgo testなどの他のコマンドは、現在のGOOSGOARCH、およびビルドタグの下で要求されたパッケージによってインポートされたパッケージを提供するためにのみgo.modを更新します(これが、go mod tidygo buildなどによって追加されなかった要件を追加する可能性のある理由の1つです)。

モジュールの依存関係自体がgo.modを持っていない場合(例えば、その依存関係自体がまだモジュールにオプトインしていないため)、またはそのgo.modファイルに1つ以上の依存関係が欠落している場合(例えば、モジュール作成者がgo mod tidyを実行しなかったため)、欠落している推移的依存関係はあなたのモジュールの要件に追加され、その依存関係がモジュール内の直接的なインポートからのものではないことを示す// indirectコメントが付加されます。

これは、直接的または間接的な依存関係から欠落しているテスト依存関係もgo.modに記録されることを意味します。(これが重要な例:go test allは、モジュールの直接的および間接的な依存関係のすべてのテストを実行します。これは、現在のバージョンの組み合わせが一緒に機能するかどうかを検証する1つの方法です。go test allを実行したときに依存関係の1つでテストが失敗した場合、再現可能なgo test allの動作を得るために、テスト依存関係の完全な情報が記録されていることが重要です。)

go.modファイルに// indirectな依存関係があるもう一つの理由は、go get -ugo get foo@1.2.3を実行した場合など、直接的な依存関係が要求する以上の間接的な依存関係をアップグレード(またはダウングレード)した場合です。Goツールはこれらの新しいバージョンを記録する場所を必要とし、それをgo.modファイルに行います(そして、依存関係のgo.modファイルを変更するためにその深くまでアクセスすることはありません)。

一般的に、上記で説明した動作は、正確な依存関係情報を記録することでモジュールが100%再現可能なビルドとテストを提供する方法の一部です。

特定のモジュールがgo.modに表示される理由が知りたい場合は、go mod why -m を実行してその質問に回答を得ることができます。要件とバージョンを検査するための他の便利なツールには、go mod graphgo list -m allがあります。

「go.sum」はロックファイルですか?なぜ「go.sum」にはもう使用していないモジュールバージョンの情報が含まれているのですか?

いいえ、go.sumはロックファイルではありません。ビルド内のgo.modファイルは、100%再現可能なビルドに十分な情報を提供します。

検証目的で、go.sumには特定のモジュールバージョンのコンテンツの期待される暗号学的チェックサムが含まれています。go.sumの詳細(go.sumを通常チェックインすべき理由を含む)については以下のFAQ、およびティップドキュメントの「モジュールのダウンロードと検証」セクションを参照してください。

さらに、モジュールのgo.sumは、ビルドで使用されるすべての直接的および間接的な依存関係のチェックサムを記録します(したがって、go.sumにはgo.modよりも多くのモジュールがリストされていることがよくあります)。

「go.sum」ファイルと「go.mod」ファイルの両方をコミットすべきですか?

通常、モジュールのgo.sumファイルはgo.modファイルとともにコミットされるべきです。

  • go.sumには、特定のモジュールバージョンのコンテンツの期待される暗号学的チェックサムが含まれています。
  • 誰かがあなたのリポジトリをクローンし、goコマンドを使用して依存関係をダウンロードした場合、ダウンロードされた依存関係のコピーとgo.sum内の対応するエントリとの間に不一致があれば、エラーが発生します。
  • さらに、go mod verifyは、ディスクキャッシュされたモジュールダウンロードのコピーがgo.sumのエントリと一致するかどうかをチェックします。
  • go.sumは、一部の代替の依存関係管理システムで使用されているようなロックファイルではないことに注意してください。(go.modは、再現可能なビルドに十分な情報を提供します)。
  • go.sumをチェックインすべき理由について、Filippo Valsordaによる非常に簡潔な根拠はこちらを参照してください。詳細については、ティップドキュメントの「モジュールのダウンロードと検証」セクションを参照してください。将来の拡張については、例えば#24117#25530で議論されているものも参照してください。

依存関係がない場合でも「go.mod」ファイルを追加する必要がありますか?

はい。これはGOPATHの外での作業をサポートし、エコシステムにモジュールへのオプトインを伝えるのに役立ちます。さらに、go.mod内のmoduleディレクティブは、コードのアイデンティティを明確に宣言する役割を果たします(これが、インポートコメントが最終的に非推奨になる理由の1つです)。もちろん、モジュールはGo 1.11では純粋なオプトイン機能です。

FAQ — 意味的インポートバージョン管理

メジャーバージョン番号がインポートパスに表示されなければならないのはなぜですか?

上記の「セマンティックインポートバージョニング」の概念セクションにあるセマンティックインポートバージョニングとインポート互換性規則に関する議論を参照してください。また、提案を発表したブログ投稿も参照してください。この投稿では、インポート互換性規則の動機と正当性について詳しく説明しています。

なぜメジャーバージョンv0, v1はインポートパスから省略されるのですか?

以前の公式提案議論のFAQにある「なぜメジャーバージョンv0, v1はインポートパスから省略されるのですか?」という質問を参照してください。

プロジェクトをメジャーバージョンv0, v1でタグ付けしたり、v2+で破壊的な変更を行ったりすることにはどのような意味がありますか?

「k8sはマイナーリリースを行うが、各マイナーリリースでGo APIを変更する」というコメントに対し、Russ Coxは以下の回答を行い、プロジェクトでv0、v1を選択することと、v2、v3、v4などで頻繁に破壊的変更を行うことの影響を強調しました。

k8sの開発サイクルなどを完全に理解しているわけではありませんが、一般的にk8sチームは、安定性に関してユーザーに何を保証するつもりかを決定/確認し、それに応じてバージョン番号を適用してそれを表現する必要があると思います。

  • API互換性に関する約束をする(これは最高のユーザーエクスペリエンスのようです!)場合は、そうし始めて1.X.Yを使用してください。
  • すべてのリリースで後方互換性のない変更を行う柔軟性を持ちながら、大規模なプログラムの異なる部分が異なるスケジュールでコードをアップグレードできるようにし、つまり、1つのプログラムで異なる部分がAPIの異なるメジャーバージョンを使用できるようにする場合は、k8s.io/client/vX/fooのようなインポートパスとともにX.Y.0を使用してください。
  • API互換性に関する約束をせず、かつ、いかなる場合でも、ビルドごとにk8sライブラリのコピーを1つだけ必要とし、その結果、ビルドのすべての部分が同じバージョンを使用せざるを得ない(たとえそれらすべてがその準備ができていなくても)ようにする場合は、0.X.Yを使用してください。

関連する注意点として、Kubernetesにはいくつかの非典型的なビルドアプローチがあります(現在はgodepに加えてカスタムラッパースクリプトが含まれます)。したがって、Kubernetesは他の多くのプロジェクトにとって不完全な例ですが、KubernetesがGo 1.11モジュールの採用に向けて進むにつれて、興味深い例になるでしょう。

モジュールは、モジュールにオプトインしていないパッケージを消費できますか?

はい。

リポジトリがモジュールにオプトインしていないが、有効なセマンティックバージョニングタグ(必須の先頭vを含む)でタグ付けされている場合、それらのセマンティックバージョニングタグはgo getで使用でき、対応するセマンティックバージョニングバージョンはインポートモジュールのgo.modファイルに記録されます。リポジトリに有効なセマンティックバージョニングタグがない場合、リポジトリのバージョンはv0.0.0-20171006230638-a6e239ea1c69のような「疑似バージョン」で記録されます(これはタイムスタンプとコミットハッシュを含み、go.modに記録されたバージョン全体に順序付けを可能にし、どの記録されたバージョンが他の記録されたバージョンよりも「後」であるかを推論しやすくするように設計されています)。

たとえば、パッケージfooの最新バージョンがv1.2.3とタグ付けされているが、foo自体はまだモジュールにオプトインしていない場合、モジュールM内でgo get fooまたはgo get foo@v1.2.3を実行すると、モジュールMのgo.modファイルに次のように記録されます。

require  foo  v1.2.3

Goツールは、追加のワークフロー(利用可能なパッチリリースにモジュールの依存関係をアップグレードするgo list -u=patch、利用可能なアップグレードを表示するgo list -u -m allなど)でも、非モジュールパッケージに利用可能なsemverタグを使用します。

モジュールにオプトインしていないv2以上のパッケージに関する追加の詳細については、次のFAQを参照してください。

モジュールは、モジュールにオプトインしていないv2+パッケージを消費できますか?「+incompatible」とは何を意味しますか?

はい、モジュールはモジュールにオプトインしていないv2以上のパッケージをインポートできます。インポートされたv2以上のパッケージに有効なsemverタグがある場合、+incompatibleサフィックスとともに記録されます。

追加の詳細

上記の「セマンティックインポートバージョニング」セクションの資料に精通してください。

一般的に有用ですが、このFAQで説明されている動作について考える際には特に重要な、いくつかの中心的な原則をまず確認すると役立ちます。

goツールがモジュールモードで動作している場合(例:GO111MODULE=on)には、以下の中心的な原則が常に真となります。

  1. パッケージのインポートパスはパッケージの識別を定義します。
    • 異なるインポートパスを持つパッケージは、異なるパッケージとして扱われます。
    • 同じインポートパスを持つパッケージは、同じパッケージとして扱われます(VCSタグがパッケージのメジャーバージョンが異なると示している場合でも、これは真です)。
  2. /vNのないインポートパスはv1またはv0モジュールとして扱われます(インポートされたパッケージがモジュールにオプトインしておらず、VCSタグがメジャーバージョンが1より大きいと示している場合でも、これは真です)。
  3. モジュールのgo.modファイルの先頭で宣言されているモジュールパス(例:module foo/v2)は、両方とも
    • そのモジュールのIDを明確に宣言するもの
    • そのモジュールが消費コードによってどのようにインポートされるべきかを明確に宣言するもの

次のFAQで説明するように、goツールがモジュールモードでない場合はこれらの原則は常に真であるとは限りませんが、goツールがモジュールモードである場合は常に真です。

要するに、+incompatibleサフィックスは、次の条件が真の場合に上記の原則2が有効であることを示します。

  • インポートされたパッケージがモジュールにオプトインしていないこと、そして
  • そのVCSタグがメジャーバージョンが1より大きいことを示していること、そして
  • 原則2がVCSタグを上書きしていること – /vNのないインポートパスはv1またはv0モジュールとして扱われること(VCSタグがそうでないと示しているにもかかわらず)。

goツールがモジュールモードの場合、モジュールではないv2以上のパッケージはセマンティックインポートバージョニングを認識していないと仮定し、そのパッケージのv1バージョン系列の(互換性のない)拡張として扱います(そして、+incompatibleサフィックスはgoツールがそうしていることを示します)。

仮に

  • oldpackageはモジュールが導入される以前のパッケージです。
  • oldpackageはモジュールにオプトインしたことがありません(したがって、go.mod自体を持っていません)。
  • oldpackageには有効なsemverタグv3.0.1があり、これが最新のタグです。

この場合、モジュールM内から例えばgo get oldpackage@latestを実行すると、モジュールMのgo.modファイルに以下が記録されます。

require  oldpackage  v3.0.1+incompatible

上記のgo getコマンドや記録されたrequireディレクティブにおいて、oldpackageの末尾に/v3が使用されていないことに注意してください。モジュールパスとインポートパスにおける/vNの使用はセマンティックインポートバージョニングの機能であり、oldpackage自体にgo.modファイルが存在しないため、oldpackageはセマンティックインポートバージョニングの受け入れと理解を知らせていません。言い換えれば、oldpackagev3.0.1semverタグを持っているにもかかわらず、oldpackageは(インポートパスでの/vNの使用など)セマンティックインポートバージョニングの権利と責任を与えられていません。なぜなら、oldpackageはまだそれを行うことを望んでいないからです。

+incompatibleサフィックスは、oldpackagev3.0.1バージョンが積極的にモジュールにオプトインしていないことを示しており、したがって、oldpackagev3.0.1バージョンはセマンティックインポートバージョニングやインポートパスでのメジャーバージョンの使用を理解していないと仮定されます。したがって、モジュールモードで動作している場合、goツールはoldpackageの非モジュールv3.0.1バージョンをoldpackageのv1バージョン系列の(互換性のない)拡張として扱い、oldpackagev3.0.1バージョンがセマンティックインポートバージョニングを認識していないと仮定します。+incompatibleサフィックスはgoツールがそうしていることを示しています。

セマンティックインポートバージョニングに従って、oldpackagev3.0.1バージョンがv1リリース系列の一部と見なされるという事実は、例えば、v1.0.0v2.0.0、およびv3.0.1のバージョンはすべて常に同じインポートパスを使用してインポートされることを意味します。

import  "oldpackage"

繰り返しになりますが、oldpackageの末尾に/v3は使用されていません。

一般的に、異なるインポートパスを持つパッケージは異なるパッケージです。この例では、oldpackageのバージョンv1.0.0v2.0.0v3.0.1はすべて同じインポートパスを使用してインポートされるため、ビルドでは同じパッケージとして扱われ(繰り返しになりますが、oldpackageはまだセマンティックインポートバージョニングにオプトインしていないため)、どのビルドでもoldpackageの単一のコピーが使用されます。(使用されるバージョンは、任意のrequireディレクティブにリストされているバージョンのうち、セマンティック的に最も高いバージョンになります。詳細は「バージョン選択」を参照)。

後にoldpackageの新しいv4.0.0リリースが作成され、モジュールを採用し、したがってgo.modファイルを含むと仮定すると、これはoldpackageがセマンティックインポートバージョニングの権利と責任を理解したことを示す信号であり、したがってモジュールベースのコンシューマはインポートパスに/v4を使用してインポートするようになります。

import  "oldpackage/v4"

そしてバージョンは次のように記録されます。

require  oldpackage/v4  v4.0.0

oldpackage/v4oldpackageとは異なるインポートパスであり、したがって異なるパッケージです。ビルド内のいくつかのコンシューマがimport "oldpackage/v4"を持ち、同じビルド内の他のコンシューマがimport "oldpackage"を持っている場合、モジュール対応ビルドでは2つのコピー(各インポートパスに1つずつ)が存在することになります。これは、モジュールの段階的な採用を可能にする戦略の一部として望ましいことです。さらに、モジュールが現在の移行段階を終えた後でも、この動作は、異なるコンシューマが異なる速度で新しいバージョンにアップグレードすることを可能にするために(例えば、大規模なビルド内で異なるコンシューマがoldpackage/v4から将来のoldpackage/v5に異なる速度でアップグレードすることを選択できるようにするために)望ましいです。

モジュールサポートが有効になっていない場合、v2+モジュールはビルドでどのように扱われますか?1.9.7+、1.10.3+、および1.11で「最小限のモジュール互換性」はどのように機能しますか?

古いGoバージョンや、まだモジュールにオプトインしていないGoコードを考慮する場合、セマンティックインポートバージョニングはv2以上のモジュールに関連する重要な後方互換性の影響を及ぼします。

上記の「セマンティックインポートバージョニング」セクションで説明されているように

  • バージョンv2以上のモジュールは、そのgo.modで宣言された自身のモジュールパスに/vNを含める必要があります。
  • モジュールベースのコンシューマー(つまり、モジュールにオプトインしたコード)は、v2以上のモジュールをインポートするためにインポートパスに/vNを含める必要があります。

しかし、エコシステムではモジュールとセマンティックインポートバージョニングの採用ペースにばらつきがあることが予想されます。

「v2+モジュールのリリース方法」セクションで詳しく説明されているように、「メジャーサブディレクトリ」アプローチでは、v2+モジュールの作成者はmymodule/v2mymodule/v3のようなサブディレクトリを作成し、適切なパッケージをこれらのサブディレクトリの下に移動またはコピーします。これは、従来のインポートパスロジック(Go 1.8や1.7などの古いGoリリースでも)が、import "mymodule/v2/mypkg"のようなインポートステートメントを見ると適切なパッケージを見つけることを意味します。したがって、「メジャーサブディレクトリ」v2+モジュールに存在するパッケージは、モジュールサポートが有効になっていない場合でも(Go 1.11を実行していてモジュールを有効にしていない場合でも、Go 1.7、1.8、1.9、1.10のような完全なモジュールサポートを持たない古いバージョンを実行している場合でも)見つかって使用されます。「メジャーサブディレクトリ」アプローチの詳細については、「v2+モジュールのリリース方法」セクションを参照してください。

このFAQの残りの部分は、「v2以上のモジュールのリリース方法」セクションで説明されている「メジャーブランチ」アプローチに焦点を当てています。「メジャーブランチ」アプローチでは、/vNサブディレクトリは作成されず、代わりにモジュールバージョン情報はgo.modファイルと、コミットにセマンティックバージョニングタグを適用することによって伝達されます(これは多くの場合master上で行われますが、異なるブランチ上で行われることもあります)。

現在の移行期間を支援するため、「最小限のモジュール互換性」がGo 1.11に導入されました。これにより、まだモジュールにオプトインしていないGoコードとの互換性が向上し、この「最小限のモジュール互換性」はGo 1.9.7および1.10.3にもバックポートされました(これらの古いGoバージョンは完全なモジュールサポートを持っていないため、事実上常に完全なモジュールモードが無効になっています)。

「最小限のモジュール互換性」の主な目的は次のとおりです。

  1. Go 1.9.7+および1.10.3+の古いGoバージョンが、インポートパスに/vNを含むセマンティックインポートバージョニングを使用するモジュールをより簡単にコンパイルできるようにし、Go 1.11でモジュールモードが無効になっている場合も同じ動作を提供すること。

  2. 古いコードがv2以上のモジュールを消費する際に、その古いコンシューマコードがv2以上のモジュールを消費する際に新しい/vNインポートパスの使用にすぐに変更することを要求せずに、v2以上のモジュールを消費できるようにすること。

  3. モジュール作成者が/vNサブディレクトリを作成することに依存しないようにすること。

追加の詳細 – 「最小限のモジュール互換性」

「最小限のモジュール互換性」は、Go 1.11でGO111MODULE=offを設定した場合や、Go 1.9.7+または1.10.3+のバージョンを使用している場合など、goツールに対して完全なモジュールモードが無効になっている場合にのみ有効になります。

v2以上のモジュール作者が/v2/vNサブディレクトリを作成しておらず、Go 1.9.7+、1.10.3+、1.11の「最小限のモジュール互換性」メカニズムに依存している場合

  • モジュールにオプトインしていないパッケージは、インポートされたv2以上のモジュールのインポートパスにメジャーバージョンを含めません。
  • 対照的に、モジュールにオプトインしたパッケージは、v2以上のモジュールをインポートするためにインポートパスにメジャーバージョンを含める必要があります。
    • モジュールにオプトインしたパッケージが、v2以上のモジュールをインポートする際にインポートパスにメジャーバージョンを含めない場合、goツールが完全なモジュールモードで動作しているときには、そのモジュールのv2以上のバージョンはインポートされません。(モジュールにオプトインしたパッケージはセマンティックインポートバージョニングを「話す」と仮定されます。fooがv2以上のバージョンを持つモジュールである場合、セマンティックインポートバージョニングではimport "foo"と言うことは、fooのv1セマンティックインポートバージョニング系列をインポートすることを意味します)。
  • 「最小限のモジュール互換性」を実装するために使用されるメカニズムは、意図的に非常に狭いです。
    • ロジック全体は、GOPATHモードで動作しているとき、/vNを含む解決不能なインポートステートメントは、そのインポートステートメントがモジュールにオプトインしたコード内にある場合(つまり、有効なgo.modファイルを持つツリー内の.goファイル内のインポートステートメント)、/vNを削除した後で再度試行されるというものです。
    • これにより、モジュール内に存在するコード内のimport "foo/v2"のようなインポートステートメントは、1.9.7+、1.10.3+、1.11のGOPATHモードでも正しくコンパイルされ、import "foo"/v2なし)と記述されたかのように解決され、余分な/v2に惑わされることなく、GOPATHにあるfooのバージョンが使用されることになります。
    • 「最小限のモジュール互換性」は、goコマンドラインで使用されるパス(go getgo listの引数など)を含む他の何も影響しません。
  • この過渡期の「最小限のモジュール認識」メカニズムは、「異なるインポートパスを持つパッケージは異なるパッケージとして扱われる」というルールを意図的に破っています。これは、非常に特定の後方互換性の目標、つまり、古いコードがv2以上のモジュールを消費する際に、変更なしでコンパイルできるようにするためです。もう少し詳しく言うと、
    • 古いコードがv2以上のモジュールを消費する唯一の方法が、まず古いコードを変更することである場合、エコシステム全体にとってより負担が大きくなるでしょう。
    • 古いコードを変更しない場合、その古いコードはv2以上のモジュールに対してモジュール以前のインポートパスで動作する必要があります。
    • 一方、モジュールにオプトインした新しいコードまたは更新されたコードは、v2以上のモジュールに対して新しい/vNインポートを使用する必要があります。
    • 新しいインポートパスは古いインポートパスと等しくありませんが、どちらも単一のビルドで機能することが許容されており、したがって、同じパッケージに解決される2つの異なる機能的なインポートパスが存在します。
    • 例えば、GOPATHモードで動作している場合、モジュールベースのコードに現れるimport "foo/v2"は、GOPATHにあるimport "foo"と同じコードに解決され、ビルドはfooの1つのコピー(特に、GOPATHのディスク上にあるバージョン)で完了します。これにより、import "foo/v2"を持つモジュールベースのコードが、1.9.7+、1.10.3+、1.11のGOPATHモードでもコンパイルできるようになります。
  • 対照的に、goツールが完全なモジュールモードで動作している場合
    • 「異なるインポートパスを持つパッケージは異なるパッケージである」というルールに例外はありません(ベンダーリングも完全なモジュールモードでこのルールに準拠するように洗練されています)。
    • 例えば、goツールが完全なモジュールモードで、fooがv2以上のモジュールである場合、import "foo"fooのv1バージョンを要求しており、import "foo/v2"fooのv2バージョンを要求しています。

go.modを作成しても、リポジトリにセマンティックバージョニングタグを適用しないとどうなりますか?

Semverはモジュールシステムの基盤です。コンシューマーにとって最高の体験を提供するために、モジュール作者はsemver VCSタグ(例: v0.1.0またはv1.2.3-rc.1)を適用することが奨励されますが、semver VCSタグは厳密には必須ではありません。

  1. モジュールは、goコマンドがドキュメントどおりに動作するために、Semver仕様に従う必要があります。これには、破壊的変更が許可される時期と方法に関するSemver仕様に従うことも含まれます。

  2. semver VCSタグを持たないモジュールは、コンシューマーによって疑似バージョンの形式でsemverバージョンを使用して記録されます。通常、これはv0のメジャーバージョンになります。ただし、モジュール作者が「メジャーサブディレクトリ」アプローチに従ってv2以上のモジュールを構築した場合は除きます。

  3. したがって、semver VCSタグを適用せず、「メジャーサブディレクトリ」を作成していないモジュールは、事実上semver v0メジャーバージョン系列であることを宣言しており、モジュールベースのコンシューマはそれらをsemver v0メジャーバージョンとして扱います。

モジュールはそれ自身の異なるバージョンに依存できますか?

モジュールは、それ自身の異なるメジャーバージョンに依存することができます。これは、異なるモジュールに依存することとほぼ同等です。これは、モジュールのメジャーバージョンを異なるメジャーバージョンのシムとして実装できるなど、さまざまな理由で有用です。

さらに、モジュールは、全く異なる2つのモジュールが循環的に依存し合うように、自身の異なるメジャーバージョンに循環的に依存することができます。

ただし、モジュールが自身の異なるバージョンに依存することを想定していない場合、それは間違いの兆候である可能性があります。たとえば、v3モジュールのパッケージをインポートしようとしている.goコードで、インポートステートメントに必須の/v3が欠落している可能性があります。その間違いは、v3モジュールが自身のv1バージョンに依存しているという形で現れることがあります。

モジュールが自身の異なるバージョンに依存していることに驚いた場合は、上記の「セマンティックインポートバージョニング」セクションと、「依存関係の期待されるバージョンが表示されない場合、何をチェックできますか?」のFAQを確認すると良いでしょう。

2つのパッケージが互いに循環的に依存することは許されないという制約は引き続き存在します。

FAQS — マルチモジュールリポジトリ

マルチモジュールリポジトリとは何ですか?

マルチモジュールリポジトリは、それぞれ独自のgo.modファイルを持つ複数のモジュールを含むリポジトリです。各モジュールは、go.modファイルを含むディレクトリから始まり、そのディレクトリとそのサブディレクトリのすべてのパッケージを再帰的に含みます。ただし、別のgo.modファイルを含むサブツリーは除外されます。

各モジュールは独自のバージョン情報を持っています。リポジトリのルートの下にあるモジュールのバージョンタグには、相対ディレクトリがプレフィックスとして含まれている必要があります。たとえば、次のリポジトリを考えてみましょう。

my-repo
`-- foo
    `-- rop
        `-- go.mod

モジュール「my-repo/foo/rop」のバージョン1.2.3のタグは「foo/rop/v1.2.3」です。

通常、リポジトリ内の1つのモジュールのパスは、他のモジュールのパスのプレフィックスとなります。たとえば、次のリポジトリを考えてみましょう。

my-repo
|-- bar
|-- foo
|   |-- rop
|   `-- yut
|-- go.mod
`-- mig
    |-- go.mod
    `-- vub

Fig. A top-level module’s path is a prefix of another module’s path.

図。トップレベルモジュールのパスは、別のモジュールのパスのプレフィックスです。

このリポジトリには2つのモジュールが含まれています。しかし、モジュール「my-repo」はモジュール「my-repo/mig」のパスのプレフィックスです。

単一のリポジトリに複数のモジュールを含めるべきですか?

このような構成でモジュールを追加、削除、バージョン管理するには、かなりの注意と熟慮が必要なため、既存のリポジトリで複数のモジュールを管理するよりも、単一モジュールリポジトリを管理する方がほぼ常に簡単でシンプルです。

Russ Coxは#26664でコメントしました。

パワーユーザー以外は、おそらく「1つのリポジトリ=1つのモジュール」という通常の慣例を採用したいと思うでしょう。リポジトリが複数のモジュールを含めることができることは、コードストレージオプションの長期的な進化にとって重要ですが、デフォルトでそうしたいことではないでしょう。

マルチモジュールがより多くの作業を必要とする2つの例

  • リポジトリルートからのgo test ./...は、リポジトリ内のすべてをテストしなくなります。
  • replaceディレクティブを介してモジュール間の関係を日常的に管理する必要があるかもしれません。

ただし、これら2つの例以外にも追加のニュアンスがあります。単一のリポジトリに複数のモジュールを持つことを検討している場合は、このサブセクションのFAQを注意深くお読みください。

リポジトリに複数のgo.modを持つことが理にかなう2つの例のシナリオ

  1. 例自体が複雑な依存関係のセットを持っている(例:小さなパッケージだが、Kubernetesでパッケージを使用する例が含まれている)場合。この場合、リポジトリに独自のgo.modを持つexampleまたは_exampleディレクトリを持つことが理にかなっています。こちらに示すように。

  2. 複雑な依存関係のセットを持つリポジトリがあるが、より少ない依存関係のクライアントAPIがある場合。場合によっては、独自のgo.modを持つapiまたはclientapiまたは類似のディレクトリを持つか、そのclientapiを独自のリポジトリに分離することが理にかなっているかもしれません。

ただし、これらの両方のケースにおいて、多数の間接的な依存関係に対するパフォーマンスまたはダウンロードサイズのためにマルチモジュールリポジトリの作成を検討している場合は、Go 1.13でデフォルトで有効になるGOPROXYをまず試すことを強くお勧めします。GOPROXYを使用すると、マルチモジュールリポジトリを作成することによって得られるであろうパフォーマンス上の利点や依存関係のダウンロードサイズ上の利点とほぼ同等になります。

マルチモジュールリポジトリにモジュールを追加することは可能ですか?

はい。ただし、この問題には2つの種類があります。

最初の種類:モジュールが追加されるパッケージがまだバージョン管理下にない場合(新しいパッケージ)。このケースは簡単です。パッケージとgo.modを同じコミットで追加し、コミットにタグを付け、プッシュします。

2番目の種類:モジュールが追加されるパスがバージョン管理下にあり、1つ以上の既存のパッケージが含まれている場合。このケースにはかなりの注意が必要です。説明のために、次のリポジトリをもう一度検討してください(より現実世界をシミュレートするためにgithub.comの場所にあります)。

github.com/my-repo
|-- bar
|-- foo
|   |-- rop
|   `-- yut
|-- go.mod
`-- mig
    `-- vub

モジュール「github.com/my-repo/mig」を追加することを考えてみましょう。もし上記と同じアプローチに従うと、パッケージ/my-repo/migは2つの異なるモジュールによって提供される可能性があります。つまり、古いバージョンの「github.com/my-repo」と、新しいスタンドアロンモジュール「github.com/my-repo/mig」です。両方のモジュールがアクティブな場合、「github.com/my-repo/mig」をインポートすると、コンパイル時に「曖昧なインポート」エラーが発生します。

これを回避する方法は、新しく追加されたモジュールを、それが「切り出された」後のバージョンで、切り出された元のモジュールに依存させることです。

上記のレポジトリで、現在の「github.com/my-repo」がv1.2.3であると仮定して、この手順を追ってみましょう。

  1. github.com/my-repo/mig/go.modを追加

    cd path-to/github.com/my-repo/mig
    go mod init github.com/my-repo/mig
    
    # Note: if "my-repo/mig" does not actually depend on "my-repo", add a blank
    # import.
    # Note: version must be at or after the carve-out.
    go mod edit -require github.com/myrepo@v1.3
    
  2. git commit

  3. git tag v1.3.0

  4. git tag mig/v1.0.0

  5. 次に、これらをテストしましょう。goコマンドは各依存モジュールをモジュールキャッシュからフェッチしようとするため、単純にgo buildgo testを行うことはできません。そのため、goコマンドにローカルコピーを使用させるためにreplaceルールを使用する必要があります。

    cd path-to/github.com/my-repo/mig
    go mod edit -replace github.com/my-repo@v1.3.0=../
    go test ./...
    go mod edit -dropreplace github.com/my-repo@v1.3.0
    
  6. git push origin master v1.3.0 mig/v1.0.0コミットと両方のタグをプッシュします。

将来的にgolang.org/issue/28835がテストステップをより簡単にするはずであることに注意してください。

また、モジュール「github.com/my-repo」からコードがマイナーバージョン間で削除されたことにも注意してください。これをメジャーな変更と見なさないのは奇妙に思えるかもしれませんが、この場合、推移的依存関係は削除されたパッケージの元のインポートパスで互換性のある実装を提供し続けています。

マルチモジュールリポジトリからモジュールを削除することは可能ですか?

はい、上記の2つのケースと同じで、同様の手順が必要です。

モジュールは別のモジュールのinternal/に依存できますか?

はい。1つのモジュール内のパッケージは、internal/パスコンポーネントまで同じパスプレフィックスを共有している限り、別のモジュールから内部パッケージをインポートできます。例えば、次のリポジトリを考えてみましょう。

my-repo
|-- foo
|   `-- go.mod
|-- go.mod
`-- internal

ここでは、パッケージfooは、モジュール「my-repo/foo」がモジュール「my-repo」に依存している限り、/my-repo/internalをインポートできます。同様に、次のリポジトリでは

my-repo
|-- foo
|   `-- go.mod
`-- internal
    `-- go.mod

ここでは、パッケージfooは、モジュール「my-repo/foo」がモジュール「my-repo/internal」に依存している限り、my-repo/internalをインポートできます。どちらの場合もセマンティクスは同じです。my-repoはmy-repo/internalとmy-repo/fooの間で共有されるパスプレフィックスであるため、パッケージfooはパッケージinternalをインポートできます。

追加のgo.modは不要なコンテンツを除外できますか?モジュールには.gitignoreファイルに相当するものはありますか?

単一のリポジトリに複数のgo.modファイルを持つ追加のユースケースの1つは、リポジトリにモジュールから除外すべきファイルがある場合です。例えば、リポジトリにはGoモジュールに不要な非常に大きなファイルがある場合や、多言語リポジトリに多くの非Goファイルがある場合があります。

ディレクトリに空のgo.modがある場合、そのディレクトリとそのすべてのサブディレクトリはトップレベルのGoモジュールから除外されます。

除外されたディレクトリに.goファイルが含まれていない場合、空のgo.modファイルを配置する以外に追加のステップは不要です。除外されたディレクトリに.goファイルが含まれている場合は、このマルチモジュールリポジトリセクションの他のFAQをまず注意深く確認してください。

FAQ — 最小バージョン選択

最小バージョン選択は、開発者が重要なアップデートを入手できないようにしませんか?

以前の公式提案議論のFAQにある「最小バージョン選択によって開発者が重要な更新を入手できなくなることはないか?」という質問を参照してください。

FAQ — 起こりうる問題

問題が発生した場合に確認できる一般的なことは何ですか?

  • go envを実行して、読み取り専用のGOMOD変数の値が空でないことを確認し、モジュールが有効になっていることを再確認してください。
    • 注: GOMODは実質的に読み取り専用のデバッグ出力であり、go envが出力するものであるため、変数として設定することはありません。
    • モジュールを有効にするためにGO111MODULE=onを設定している場合、誤って複数形のGO111MODULES=onになっていないか再確認してください。(この機能は「モジュール」と呼ばれることが多いため、人々は自然にSを追加してしまうことがあります)。
  • ベンダーリングが使用されると想定される場合、-mod=vendorフラグがgo buildなどにも渡されているか、またはGOFLAGS=-mod=vendorが設定されているかを確認してください。
    • モジュールはデフォルトでvendorディレクトリを無視します。ただし、goツールにvendorを使用するように指示した場合を除きます。
  • ビルドで選択された実際のバージョンのリストを確認するには、go list -m allをチェックすることが頻繁に役立ちます。
    • go list -m allは、go.modファイルを見るだけの場合と比較して、通常より詳細な情報を提供します。
  • go get fooが何らかの形で失敗したり、go buildが特定のパッケージfooで失敗したりする場合、go get -v fooまたはgo get -v -x fooの出力を確認すると役立つことがよくあります。
    • 一般的に、go getgo buildよりも詳細なエラーメッセージを提供することがよくあります。
    • go get-vフラグは、より詳細な情報を出力するように要求しますが、リモートリポジトリの構成方法によっては、404エラーなどの特定の「エラー」が予想される場合があることに注意してください。
    • 問題の性質がまだ明確でない場合、より詳細なgo get -v -x fooも試すことができます。これは発行されているgitまたは他のVCSコマンドも表示します。(必要であれば、トラブルシューティング目的でgoツールのコンテキスト外で同じgitコマンドを実行することもよくできます)。
  • 特に古いgitバージョンを使用していないか確認できます。
    • 古いバージョンのgitは、vgoプロトタイプとGo 1.11ベータ版で一般的な問題の原因でしたが、GA 1.11でははるかに少なくなりました。
  • Go 1.11のモジュールキャッシュは、主に以前にネットワークの問題があった場合や複数のgoコマンドが並行して実行された場合(Go 1.12で対処された#26794を参照)に、様々なエラーを引き起こすことがあります。トラブルシューティングの手順として、$GOPATH/pkg/modをバックアップディレクトリにコピーし(後でさらに調査が必要な場合に備えて)、go clean -modcacheを実行し、その後元の問題が解決したかどうかを確認することができます。
  • Dockerを使用している場合、Dockerの外でその動作を再現できるか確認すると役立ちます(そして、その動作がDocker内でのみ発生する場合は、上記の箇条書きリストを、Docker内と外での結果を比較するための出発点として使用できます)。

現在調査しているエラーは、ビルドで特定のモジュールまたはパッケージの期待されるバージョンがないことによって引き起こされる二次的な問題である可能性があります。したがって、特定のエラーの原因が不明な場合は、次のFAQで説明されているようにバージョンをスポットチェックすると役立ちます。

依存関係の期待するバージョンが表示されない場合、何を確認できますか?

  1. 最初の良いステップはgo mod tidyを実行することです。これにより問題が解決する可能性もありますが、go.modファイルを.goソースコードと整合性のある状態に保ち、その後の調査を容易にするのにも役立ちます。(もしgo mod tidy自体が依存関係のバージョンを予期しない方法で変更した場合、まず「go mod tidy」に関するこのFAQをお読みください。それでも解決しない場合は、go.modをリセットし、go list -mod=readonly allを実行してみてください。これにより、そのバージョンの変更を必要としていたものが何であるかについて、より具体的なメッセージが表示される可能性があります)。

  2. 通常、2番目のステップは、go list -m allをチェックして、ビルド用に選択された実際のバージョンリストを確認することです。go list -m allは、間接的な依存関係を含め、共有された依存関係のバージョン解決後も、最終的に選択されたバージョンを表示します。また、replaceおよびexcludeディレクティブの結果も表示します。

  3. 次に良いステップは、go mod graphまたはgo mod graph | grep の出力を調べることです。go mod graphは、モジュール要件グラフを出力します(置換も考慮に入れます)。出力の各行には2つのフィールドがあります。最初の列は消費モジュールであり、2番目の列はそのモジュールの要件の1つです(その消費モジュールによって要求されるバージョンを含みます)。これは、特定の依存関係をどのモジュールが要求しているかをすばやく確認する方法であり、ビルド内の異なるコンシューマから異なる要求バージョンを持つ依存関係がある場合にも役立ちます(そしてその場合、上記の「バージョン選択」セクションで説明されている動作に精通していることが重要です)。

go mod why -m もここで有用ですが、通常は、依存関係がなぜ含まれているのか(依存関係がなぜ特定のバージョンになるのかではなく)を見るのに役立ちます。

go listは、必要に応じてモジュールを調査するのに役立つ、より多くの種類のクエリを提供します。一例として、テスト専用の依存関係を除外して、ビルドで使用される正確なバージョンを表示する次のものがあります。

go list -deps -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' ./... | sort -u

モジュールを調査するためのより詳細なコマンドと例のセットは、実行可能な「Go Modules by Example」のウォークスルーで確認できます。

予期しないバージョンの原因の1つは、意図しない無効なまたは予期しないgo.modファイルが作成されたこと、または関連する間違い(例:モジュールのv2.0.1バージョンが、必須の/v2なしでgo.modで誤ってmodule fooと宣言した。v3モジュールをインポートすることを意図した.goコードのインポートステートメントに必須の/v3が欠落している。v4モジュールのgo.modrequireステートメントに必須の/v4が欠落している)によるものです。したがって、発生している特定の問題の原因が不明な場合は、まず上記の「go.mod」および「セマンティックインポートバージョニング」セクションの資料を再読し(これらにはモジュールが従うべき重要なルールが含まれているため)、次に最も関連性の高いgo.modファイルとインポートステートメントを数分かけてスポットチェックすると良いでしょう。

なぜ「パッケージfooを提供するメインモジュールが見つかりません」というエラーが表示されるのですか?

これは、いくつかの異なる根本原因で発生する可能性のある一般的なエラーメッセージです。

場合によっては、このエラーは単にパスの入力ミスによるものなので、最初の手順として、エラーメッセージに記載されている詳細に基づいて誤ったパスがないか再確認するべきでしょう。

まだ行っていない場合は、次のステップとしてgo get -v fooまたはgo get -v -x fooを試すことがよくあります。

  • 一般的に、go getgo buildよりも詳細なエラーメッセージを提供することがよくあります。
  • 詳細については、このセクションの最初のトラブルシューティングFAQ上記を参照してください。

その他の考えられる原因

  • go buildまたはgo build .を実行したが、現在のディレクトリに.goソースファイルがない場合、cannot find module providing package fooというエラーが表示されることがあります。この問題に遭遇している場合、解決策はgo build ./...のような別の呼び出しである可能性があります(ここで./...は現在のモジュール内のすべてのパッケージに一致するように展開されます)。#27122を参照してください。

  • Go 1.11のモジュールキャッシュは、ネットワークの問題や複数のgoコマンドが並行して実行された場合などを含め、このエラーを引き起こす可能性があります。これはGo 1.12で解決されています。詳細と可能な修正手順については、このセクションの最初のトラブルシューティングFAQ上記を参照してください。

なぜ「go mod init」が「ソースディレクトリのモジュールパスを決定できません」というエラーを出すのですか?

引数なしのgo mod initは、VCSメタデータなどの様々なヒントに基づいて適切なモジュールパスを推測しようとします。しかし、go mod initが常に適切なモジュールパスを推測できるとは限りません。

go mod initがこのエラーを出す場合、そのヒューリスティックは推測できなかったため、自分でモジュールパスを指定する必要があります(例:go mod init github.com/you/hello)。

モジュールにオプトインしていない複雑な依存関係に問題があります。その現在の依存関係マネージャーからの情報を使用できますか?

はい。これにはいくつかの手動手順が必要ですが、より複雑なケースで役立つことがあります。

Go バージョン 1.21 以前は、以前の依存関係マネージャー形式をgo.mod形式に変換しようとしていました。したがって、次の手順では 1.21.13 以前が必要になります。GOTOOLCHAIN=go1.21.13で実行するか、手動で古い Go バージョンをインストールする必要があります。

自身のモジュールを初期化する際にgo mod initを実行すると、Gopkg.lockglide.lockvendor.jsonなどの設定ファイルを対応するrequireディレクティブを含むgo.modファイルに変換することで、以前の依存関係マネージャーから自動的に変換されます。例えば、既存のGopkg.lockファイルの情報は通常、直接的および間接的な依存関係のすべてのバージョン情報を記述しています。

しかし、代わりに、まだモジュールにオプトインしていない新しい依存関係を追加する場合、その新しい依存関係が使用していた以前の依存関係マネージャーから同様の自動変換プロセスはありません。もしその新しい依存関係自体が、破壊的変更があった非モジュール依存関係を持っている場合、場合によっては互換性の問題を引き起こす可能性があります。言い換えれば、新しい依存関係の以前の依存関係マネージャーは自動的に使用されず、場合によっては間接的な依存関係に問題を引き起こす可能性があります。

1つのアプローチは、問題のある非モジュール直接依存関係でgo mod initを実行して現在の依存関係マネージャーから変換し、結果として得られる一時的なgo.modからのrequireディレクティブを使用してモジュール内のgo.modを設定または更新することです。

例えば、github.com/some/nonmoduleが、現在別の依存関係マネージャーを使用しているモジュールの問題のある直接依存関係である場合、次のようにすることができます。

$ git clone -b v1.2.3 https://github.com/some/nonmodule /tmp/scratchpad/nonmodule
$ cd /tmp/scratchpad/nonmodule
$ go mod init
$ cat go.mod

一時的なgo.modから得られたrequire情報は、モジュールの実際のgo.modに手動で移動するか、このユースケースをターゲットにしたコミュニティツールであるhttps://github.com/rogpeppe/gomodmergeの使用を検討することができます。さらに、手動でクローンしたバージョンと一致させるために、実際のgo.modrequire github.com/some/nonmodule v1.2.3を追加する必要があります。

Dockerのこの手法に従う具体的な例は、この#28489コメントにあります。これは、github.com/sirupsen/logrusgithub.com/Sirupsen/logrusの間で大文字小文字の区別に関する問題を回避するために、一貫したDocker依存関係のセットを取得する方法を示しています。

インポートパスと宣言されたモジュールIDの不一致によって引き起こされる「go.modの解析: 予期しないモジュールパス」および「モジュール要件の読み込みエラー」をどのように解決できますか?

なぜこのエラーが発生するのですか?

一般に、モジュールはそのgo.mod内のmoduleディレクティブ(例:module example.com/m)を通じて自身のアイデンティティを宣言します。これはそのモジュールの「モジュールパス」であり、goツールは宣言されたモジュールパスとコンシューマが使用するインポートパスとの一貫性を強制します。モジュールのgo.modファイルがmodule example.com/mと記述されている場合、コンシューマはそのモジュールパスで始まるインポートパス(例:import "example.com/m"またはimport "example.com/m/sub/pkg")を使用してそのモジュールからパッケージをインポートする必要があります。

Goコマンドは、コンシューマが使用するインポートパスと、対応する宣言されたモジュールパスとの間に不一致がある場合、parsing go.mod: unexpected module pathという致命的なエラーを報告します。さらに、場合によっては、Goコマンドはその後、より一般的なerror loading module requirementsエラーを報告します。

このエラーの最も一般的な原因は、名前の変更(例:github.com/Sirupsen/logrusからgithub.com/sirupsen/logrus)、またはバニティインポートパスのためにモジュールが以前に2つの異なる名前で使用されていた場合(例:github.com/golang/syncと推奨されるgolang.org/x/sync)です。

これは、依存関係が古い名前(例:github.com/Sirupsen/logrus)または非正規名(例:github.com/golang/sync)でまだインポートされているが、その依存関係がその後モジュールを採用し、現在その正規名をgo.modで宣言している場合に問題を引き起こす可能性があります。この場合のエラーは、アップグレード時に、モジュールのアップグレードされたバージョンが古いインポートパスと一致しない正規モジュールパスを宣言していることが判明したときにトリガーされる可能性があります。

問題シナリオ例

  • github.com/Quasilyte/go-consistentに間接的に依存しています。
  • プロジェクトがモジュールを採用し、その後名前をgithub.com/quasilyte/go-consistentに変更しました(Qを小文字のqに変更)。これは破壊的変更です。GitHubは古い名前から新しい名前への転送を行います。
  • go get -uを実行して、すべての直接的および間接的な依存関係をアップグレードしようとします。
  • github.com/Quasilyte/go-consistentのアップグレードが試行されますが、見つかった最新のgo.modにはmodule github.com/quasilyte/go-consistentと記述されています。
  • 全体的なアップグレード操作がエラーで完了できません。

go: github.com/Quasilyte/go-consistent@v0.0.0-20190521200055-c6f3937de18c: parsing go.mod: unexpected module path “github.com/quasilyte/go-consistent” go get: error loading module requirements

解決

エラーの最も一般的な形式は次のとおりです。

go: example.com/some/OLD/name@vX.Y.Z: parsing go.mod: unexpected module path “example.com/some/NEW/name”

エラーの右側にある example.com/some/NEW/name のリポジトリにアクセスすると、go.mod ファイルで最新リリースまたは master をチェックして、それが go.mod の最初の行で module example.com/some/NEW/name と宣言されているか確認できます。もしそうであれば、それは「古いモジュール名」と「新しいモジュール名」の問題を見ていることのヒントになります。

このセクションの残りの部分では、以下の手順を順に実行することで、このエラーの「古い名前」と「新しい名前」の形式を解決することに焦点を当てます。

  1. ご自身のコードをチェックして、example.com/some/OLD/name を使ってインポートしているかどうかを確認してください。もしそうであれば、コードを更新して example.com/some/NEW/name を使ってインポートするようにしてください。

  2. アップグレード中にこのエラーが発生した場合、Go の tip バージョンを使用してアップグレードを試してください。tip バージョンには、この問題を回避できる、より対象を絞ったアップグレードロジック (#26902) があり、この状況に対するより良いエラーメッセージが表示されることもよくあります。tip / 1.13 の go get 引数は 1.12 とは異なることに注意してください。tip を取得し、それを使用して依存関係をアップグレードする例を以下に示します。

go get golang.org/dl/gotip && gotip download
gotip get -u all
gotip mod tidy

問題のある古いインポートは間接的な依存関係にあることが多いため、tip でアップグレードし、その後 go mod tidy を実行することで、問題のあるバージョンを超えてアップグレードできることがよくあります。その後、go.mod から問題のあるバージョンが不要として削除され、Go 1.12 または 1.11 を日常的に使用に戻したときに機能する状態になります。たとえば、github.com/golang/lintgolang.org/x/lint の問題を解決するために、このアプローチが こちら でどのように機能したかを確認してください。

  1. go get -u foo または go get -u foo@latest の実行中にこのエラーが発生した場合は、-u を削除してみてください。これにより、foo@latest が使用する依存関係のセットが得られますが、foo の作者が foo をリリースしたときに動作することを検証したであろうバージョンを超えて foo の依存関係はアップグレードされません。これは、foo の直接的および間接的な依存関係の一部がまだ semver またはモジュールを採用していない可能性があるこの移行期間中は特に重要です。(よくある間違いは、go get -u foofoo の最新バージョンのみを取得すると考えることです。実際には、go get -u foo または go get -u foo@latest-u は、foo の直接的および間接的な依存関係のすべての最新バージョンを取得することを意味します。これは望ましいことかもしれませんが、深い間接的な依存関係のためにそれ以外で失敗している場合はそうではないかもしれません)。

  2. 上記の手順でエラーが解決しない場合、次のアプローチは少し複雑ですが、ほとんどの場合、このエラーの「古い名前」と「新しい名前」の形式を解決するために機能するはずです。これは、エラーメッセージ自体からの情報のみと、VCS履歴を少し見るだけで済みます。

    4.1. example.com/some/NEW/name リポジトリにアクセスします。

    4.2. そこに go.mod ファイルが導入された時期を特定します (たとえば、go.mod の blame または履歴ビューを確認します)。

    4.3. そこに go.mod ファイルが導入される直前のリリースまたはコミットを選択します。

    4.4. 自分の go.mod ファイルに、replace ステートメントの両側に古い名前を使用して replace ステートメントを追加します: replace example.com/some/OLD/name => example.com/some/OLD/name <version-just-before-go.mod>。以前の例では、github.com/Quasilyte/go-consistent が古い名前で、github.com/quasilyte/go-consistent が新しい名前でしたが、go.mod はコミット 00c5b0cf371a で最初に導入されました。そのリポジトリは semver タグを使用していないため、直前のコミット 00dd7fb039e を取得し、replace の両側に古い大文字の Quasilyte 名を使用して replace に追加します。

replace github.com/Quasilyte/go-consistent => github.com/Quasilyte/go-consistent 00dd7fb039e

この replace ステートメントにより、go.mod が存在する場合に古い名前が新しい名前にアップグレードされるのを効果的に防ぐことで、問題のある「古い名前」と「新しい名前」の不一致を超えてアップグレードすることができます。通常、go get -u などによるアップグレードはエラーを回避できます。アップグレードが完了したら、まだ古い名前をインポートしているものがないか確認できます (例: go mod graph | grep github.com/Quasilyte/go-consistent)。もしなければ、replace を削除できます。(これが機能する理由は、アップグレードが完了していれば最終結果では使用されない可能性のある古い問題のあるインポートパスが使用されている場合でも、アップグレード自体が失敗する可能性があるためです。これは #30831 で追跡されています)。

  1. 上記の手順で問題が解決しない場合、問題のある古いインポートパスが、1つ以上の依存関係の最新バージョンでまだ使用されている可能性があります。この場合、問題のある古いインポートパスをまだ使用しているのが誰であるかを特定し、問題のあるインポーターが現在正当なインポートパスを使用するように変更するよう求める問題を特定または開設することが重要です。上記のステップ2.の gotip を使用すると、問題のあるインポーターを特定できる場合がありますが、特にアップグレードの場合 (#30661) は常にそうではありません。問題のある古いインポートパスを使用してインポートしているのが誰であるかが不明な場合は、通常、クリーンなモジュールキャッシュを作成し、エラーを引き起こす操作を実行し、その後、モジュールキャッシュ内で古い問題のあるインポートパスを grep することで見つけることができます。例えば
export GOPATH=$(mktemp -d)
go get -u foo               # perform operation that generates the error of interest
cd $GOPATH/pkg/mod
grep -R --include="*.go" github.com/Quasilyte/go-consistent
  1. これらの手順で問題が解決しない場合、または循環参照のために古い問題のあるインポートパスへの参照を削除できないように見えるプロジェクトのメンテナーである場合は、問題の詳細な説明が別の wiki ページにありますので、そちらを参照してください。

最後に、上記の手順は、根本的な「古い名前」と「新しい名前」の問題を解決する方法に焦点を当てています。しかし、go.mod が間違った場所に配置されているか、単にモジュールパスが間違っている場合にも、同じエラーメッセージが表示されることがあります。その場合、そのモジュールのインポートは常に失敗するはずです。新しく作成したばかりで、以前に正常にインポートされたことのない新しいモジュールをインポートしている場合は、go.mod ファイルが正しく配置されており、その場所に一致する適切なモジュールパスを持っていることを確認する必要があります。(最も一般的なアプローチは、リポジトリごとに1つの go.mod を使用し、その1つの go.mod ファイルをリポジトリのルートに配置し、リポジトリ名を module ディレクティブで宣言されたモジュールパスとして使用することです)。詳細については、「go.mod」セクションを参照してください。

なぜ「go build」にはgccが必要で、net/httpなどの事前ビルド済みパッケージが使用されないのですか?

要するに

ビルド済みのパッケージは非モジュールビルドであり、再利用できません。申し訳ありません。とりあえずcgoを無効にするか、gccをインストールしてください。

これは、モジュールを選択した場合にのみ問題となります (例: GO111MODULE=on を介して)。追加の議論については #26988 を参照してください。

モジュールはimport "./subdir"のような相対インポートで動作しますか?

いいえ。 #26645 を参照してください。これには、以下の内容が含まれています。

モジュールでは、サブディレクトリについに名前が付きます。親ディレクトリが「module m」と言う場合、サブディレクトリは「m/subdir」としてインポートされ、もはや「./subdir」ではありません。

必要なファイルの一部が、生成されたベンダーディレクトリに存在しない場合があります

.go ファイルのないディレクトリは、go mod vendor によって vendor ディレクトリ内にコピーされません。これは設計によるものです。

つまり、特定のベンダー挙動はさておき、Go ビルドの全体的なモデルは、パッケージをビルドするために必要なファイルは .go ファイルがあるディレクトリに存在すべきである、ということです。

cgo の例を使用すると、他のディレクトリの C ソースコードを変更しても再ビルドはトリガーされず、代わりにビルドは古いキャッシュエントリを使用します。cgo のドキュメントには、現在 含まれています

他のディレクトリにあるファイルの変更はパッケージの再コンパイルを引き起こさないため、パッケージのGo以外のすべてのソースコードは、サブディレクトリではなく、パッケージディレクトリに保存する必要があります

コミュニティツール https://github.com/goware/modvendor を使用すると、モジュールから .c.h.s.proto、その他のファイルの完全なセットを vendor ディレクトリに簡単にコピーできます。これは役立つかもしれませんが、.go ファイルのあるディレクトリの外にパッケージをビルドするために必要なファイルがある場合、一般的に (ベンダー化に関係なく) Go ビルドが適切に処理されていることを確認するために、ある程度の注意が必要です。

#26366 での追加の議論を参照してください。

従来のベンダー管理とは異なるアプローチとして、モジュールキャッシュをチェックインする方法があります。これは従来のベンダー管理と同様の利点をもたらし、ある意味でより忠実なコピーとなります。このアプローチは、「Go Modules by Example」の ウォークスルーで説明されています。


このコンテンツはGo Wikiの一部です。