はじめに

この記事はシリーズの第3部です。

注: モジュールの開発に関するドキュメントについては、モジュールの開発と公開を参照してください。

この記事では、他のモジュールが依存できるようにモジュールを書く方法と公開する方法について説明します。

ご注意ください:この記事はv1までの開発をカバーしています。v2に興味がある場合は、Goモジュール:v2以降を参照してください。

この記事では、例としてGitを使用しています。MercurialBazaar、およびその他もサポートされています。

プロジェクトのセットアップ

この記事では、例として使用する既存のプロジェクトが必要です。したがって、Goモジュールの使用の記事の最後のファイルから始めてください:

  1. $ cat go.mod
  2. module example.com/hello
  3. go 1.12
  4. require rsc.io/quote/v3 v3.1.0
  5. $ cat go.sum
  6. golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
  7. golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
  8. rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
  9. rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
  10. rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
  11. rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
  12. $ cat hello.go
  13. package hello
  14. import "rsc.io/quote/v3"
  15. func Hello() string {
  16. return quote.HelloV3()
  17. }
  18. func Proverb() string {
  19. return quote.Concurrency()
  20. }
  21. $ cat hello_test.go
  22. package hello
  23. import (
  24. "testing"
  25. )
  26. func TestHello(t *testing.T) {
  27. want := "Hello, world."
  28. if got := Hello(); got != want {
  29. t.Errorf("Hello() = %q, want %q", got, want)
  30. }
  31. }
  32. func TestProverb(t *testing.T) {
  33. want := "Concurrency is not parallelism."
  34. if got := Proverb(); got != want {
  35. t.Errorf("Proverb() = %q, want %q", got, want)
  36. }
  37. }
  38. $

次に、新しいgitリポジトリを作成し、初期コミットを追加します。自分のプロジェクトを公開する場合は、LICENSEファイルを含めることを忘れないでください。go.modを含むディレクトリに移動し、リポジトリを作成します:

  1. $ git init
  2. $ git add LICENSE go.mod go.sum hello.go hello_test.go
  3. $ git commit -m "hello: initial commit"
  4. $

セマンティックバージョンとモジュール

  1. セマンティックバージョンは`````vMAJOR.MINOR.PATCH`````の形式を持ちます。
  2. - 公開APIに[後方互換性のない](https://golang.org/doc/go1compat)変更を加えた場合は、`````MAJOR`````バージョンをインクリメントします。これは絶対に必要な場合にのみ行うべきです。
  3. - APIに対して後方互換性のある変更を加えた場合(依存関係の変更や新しい関数、メソッド、構造体フィールド、または型の追加など)、`````MINOR`````バージョンをインクリメントします。
  4. - モジュールの公開APIや依存関係に影響を与えない小さな変更(バグ修正など)を行った後は、`````PATCH`````バージョンをインクリメントします。
  5. ハイフンとドットで区切られた識別子を追加することで、プレリリースバージョンを指定できます(例:`````v1.0.1-alpha`````または`````v2.2.2-beta.2`````)。通常のリリースは、`````go`````コマンドによってプレリリースバージョンよりも優先されるため、ユーザーはモジュールに通常のリリースがある場合、プレリリースバージョンを明示的に要求する必要があります(例:`````go get example.com/[email protected]`````)。
  6. `````v0`````メジャーバージョンとプレリリースバージョンは、後方互換性を保証しません。これにより、ユーザーに対して安定性の約束をする前にAPIを洗練させることができます。ただし、`````v1`````メジャーバージョン以降は、そのメジャーバージョン内で後方互換性が必要です。
  7. `````go.mod`````で参照されるバージョンは、リポジトリ内でタグ付けされた明示的なリリース(例:`````v1.5.2`````)であるか、特定のコミットに基づく[擬似バージョン](7e2d4c469f5d263b.md#pseudo-versions)(例:`````v0.0.0-20170915032832-14c0d48ead0c`````)である可能性があります。擬似バージョンはプレリリースバージョンの特別なタイプです。擬似バージョンは、ユーザーがセマンティックバージョンタグを公開していないプロジェクトに依存する必要がある場合や、まだタグ付けされていないコミットに対して開発する必要がある場合に便利ですが、ユーザーは擬似バージョンが安定したり十分にテストされたAPIを提供するとは限らないことを前提にすべきではありません。モジュールに明示的なバージョンでタグ付けすることは、特定のバージョンが完全にテストされて使用可能であることをユーザーに示します。
  8. リポジトリにバージョンでタグ付けを開始したら、モジュールを開発する際に新しいリリースにタグ付けを続けることが重要です。ユーザーがモジュールの新しいバージョンを要求すると(`````go get -u`````または`````go get example.com/hello`````を使用)、`````go`````コマンドは、たとえそのバージョンが数年前のものであり、主要ブランチから多くの変更がある場合でも、利用可能な最も大きなセマンティックリリースバージョンを選択します。新しいリリースにタグ付けを続けることで、進行中の改善をユーザーに提供できます。
  9. リポジトリからバージョンタグを削除しないでください。バージョンにバグやセキュリティの問題が見つかった場合は、新しいバージョンをリリースしてください。削除したバージョンに依存している人々のビルドが失敗する可能性があります。同様に、一度バージョンをリリースしたら、それを変更したり上書きしたりしないでください。[モジュールミラーとチェックサムデータベース](https://golang.org/blog/module-mirror-launch)は、モジュール、そのバージョン、および署名された暗号ハッシュを保存して、特定のバージョンのビルドが時間の経過とともに再現可能であることを保証します。
  10. <a name="v0-the-initial-unstable-version"></a>
  11. ## v0: 初期の不安定なバージョン
  12. モジュールに`````v0`````セマンティックバージョンでタグ付けしましょう。`````v0`````バージョンは安定性の保証を行わないため、ほとんどのプロジェクトは公開APIを洗練させる際に`````v0`````から始めるべきです。
  13. 新しいバージョンにタグ付けするにはいくつかのステップがあります:
  14. - 1*.* `````go mod tidy`````を実行して、モジュールがもはや必要でない依存関係を削除します。
  15. - 2*.* すべてが正常に動作していることを確認するために、`````go test ./...`````を最終的に実行します。
  16. - 3*.* [`````git tag`````](https://git-scm.com/docs/git-tag)を使用して新しいバージョンでプロジェクトにタグ付けします。
  17. - 4*.* 新しいタグをオリジンリポジトリにプッシュします。
  18. ``````bash
  19. $ go mod tidy
  20. $ go test ./...
  21. ok example.com/hello 0.015s
  22. $ git add go.mod go.sum hello.go hello_test.go
  23. $ git commit -m "hello: changes for v0.1.0"
  24. $ git tag v0.1.0
  25. $ git push origin v0.1.0
  26. $
  27. `

これで、他のプロジェクトはv0.1.0example.com/helloに依存できます。自分のモジュールについては、go list -m example.com/[email protected]を実行して最新のバージョンが利用可能であることを確認できます(この例のモジュールは存在しないため、利用可能なバージョンはありません)。最新のバージョンがすぐに表示されない場合は、Goモジュールプロキシ(Go 1.13以降のデフォルト)を使用している場合、プロキシが新しいバージョンを読み込む時間を与えるために数分後に再試行してください。

公開APIに追加したり、v0モジュールに破壊的な変更を加えたり、依存関係のマイナーまたはバージョンをアップグレードした場合は、次のリリースのためにMINORバージョンをインクリメントします。たとえば、v0.1.0の次のリリースはv0.2.0になります。

既存のバージョンにバグを修正した場合は、PATCHバージョンをインクリメントします。たとえば、v0.1.0の次のリリースはv0.1.1になります。

v1: 最初の安定版

モジュールのAPIが安定していることを確信したら、v1.0.0をリリースできます。v1メジャーバージョンは、ユーザーに対してモジュールのAPIに互換性のない変更が行われないことを伝えます。彼らは新しいv1マイナーおよびパッチリリースにアップグレードでき、そのコードは壊れないはずです。関数やメソッドのシグネチャは変更されず、エクスポートされた型は削除されず、などです。APIに変更がある場合、それらは後方互換性があり(たとえば、構造体に新しいフィールドを追加するなど)、新しいマイナーリリースに含まれます。バグ修正(たとえば、セキュリティ修正)がある場合、それらはパッチリリースに含まれます(またはマイナーリリースの一部として)。

時には、後方互換性を維持することが不自然なAPIにつながることがあります。それは問題ありません。不完全なAPIは、ユーザーの既存のコードを壊すよりも良いです。

標準ライブラリのstringsパッケージは、APIの一貫性のコストで後方互換性を維持する良い例です。

  • Splitは、文字列を区切り文字で分割し、その区切り文字の間の部分文字列のスライスを返します。
  • SplitNは、返す部分文字列の数を制御するために使用できます。

しかし、Replaceは、Splitとは異なり、文字列を置き換えるインスタンスの数を最初から数えました。

SplitSplitNを考えると、ReplaceReplaceNのような関数が期待されるでしょう。しかし、私たちは呼び出し元を壊さないように既存のReplaceを変更することができませんでした。したがって、Go 1.12では新しい関数ReplaceAllを追加しました。結果として得られたAPIは少し奇妙ですが、SplitReplaceが異なる動作をするため、その不一致は破壊的な変更よりも良いです。

  1. `````v1`````にタグ付けするプロセスは、`````v0`````バージョンにタグ付けするプロセスと同じです:`````go mod tidy``````````go test ./...`````を実行し、バージョンにタグ付けし、タグをオリジンリポジトリにプッシュします:
  2. ``````bash
  3. $ go mod tidy
  4. $ go test ./...
  5. ok example.com/hello 0.015s
  6. $ git add go.mod go.sum hello.go hello_test.go
  7. $ git commit -m "hello: changes for v1.0.0"
  8. $ git tag v1.0.0
  9. $ git push origin v1.0.0
  10. $
  11. `

この時点で、v1example.com/hello APIは確立されました。これは、私たちのAPIが安定していることを皆に伝え、安心して使用できることを示します。

結論

この記事では、セマンティックバージョンでモジュールにタグ付けするプロセスと、v1をリリースするタイミングについて説明しました。今後の記事では、v2以降のモジュールの維持と公開について説明します。

Goにおける依存関係管理の未来を形作るためにフィードバックを提供し、バグレポート体験レポートをお送りください。

Goモジュールの改善に向けたすべてのフィードバックと支援に感謝します。