The Go Blog
Goモジュールの公開
はじめに
この記事はシリーズのパート3です。
- パート1 — Go Modules の使用
- パート2 — Go Modules への移行
- パート3 — Goモジュールの公開 (この記事)
- パート4 — Go Modules: v2 以降
- パート5 — モジュールの互換性を保つ
注: モジュールの開発に関するドキュメントについては、モジュールの開発と公開をご覧ください。
この記事では、他のモジュールが依存できるようにモジュールを記述して公開する方法について説明します。
ご注意ください: この記事はv1までの開発を対象としています。v2にご興味がある場合は、「Go Modules: v2 and Beyond」をご覧ください。
この記事では例としてGitを使用しています。Mercurial、Bazaarなどもサポートされています。
プロジェクトのセットアップ
この記事では、例として使用する既存のプロジェクトが必要です。まず、「Goモジュールの使用」の記事のファイルから始めます。
$ cat go.mod
module example.com/hello
go 1.12
require rsc.io/quote/v3 v3.1.0
$ cat go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
$ cat hello.go
package hello
import "rsc.io/quote/v3"
func Hello() string {
return quote.HelloV3()
}
func Proverb() string {
return quote.Concurrency()
}
$ cat 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)
}
}
func TestProverb(t *testing.T) {
want := "Concurrency is not parallelism."
if got := Proverb(); got != want {
t.Errorf("Proverb() = %q, want %q", got, want)
}
}
$
次に、新しいgitリポジトリを作成し、初期コミットを追加します。自分のプロジェクトを公開する場合は、必ずLICENSEファイルを含めてください。go.modを含むディレクトリに移動してから、リポジトリを作成します。
$ git init
$ git add LICENSE go.mod go.sum hello.go hello_test.go
$ git commit -m "hello: initial commit"
$
セマンティックバージョンとモジュール
go.mod内のすべての必須モジュールにはセマンティックバージョンがあり、これはモジュールをビルドするために使用するその依存関係の最小バージョンです。
セマンティックバージョンはvMAJOR.MINOR.PATCHの形式です。
- モジュールの公開APIに後方互換性のない変更を加える場合は、
MAJORバージョンをインクリメントします。これは、絶対に必要な場合にのみ行うべきです。 - 依存関係の変更や新しい関数、メソッド、構造体フィールド、または型の追加など、APIに後方互換性のある変更を加える場合は、
MINORバージョンをインクリメントします。 - バグ修正など、モジュールの公開APIや依存関係に影響しない軽微な変更を加えた後は、
PATCHバージョンをインクリメントします。
ハイフンとドットで区切られた識別子(例: v1.0.1-alphaまたはv2.2.2-beta.2)を追加することで、プレリリースバージョンを指定できます。goコマンドはプレリリースバージョンよりも通常のリリースを優先するため、モジュールに通常のリリースがある場合、ユーザーは明示的にプレリリースバージョンを要求する必要があります(例: go get example.com/hello@v1.0.1-alpha)。
v0メジャーバージョンとプレリリースバージョンは、後方互換性を保証しません。これにより、ユーザーに安定性の約束をする前にAPIを改良できます。ただし、v1メジャーバージョン以降は、そのメジャーバージョン内で後方互換性が必要です。
go.modで参照されるバージョンは、リポジトリにタグ付けされた明示的なリリース(例: v1.5.2)であるか、特定のコミットに基づいた擬似バージョン(例: v0.0.0-20170915032832-14c0d48ead0c)である場合があります。擬似バージョンは特別な種類のプレリリースバージョンです。擬似バージョンは、ユーザーがセマンティックバージョンのタグを公開していないプロジェクトに依存する必要がある場合や、まだタグ付けされていないコミットに対して開発する場合に役立ちますが、ユーザーは擬似バージョンが安定した、十分にテストされたAPIを提供すると仮定すべきではありません。モジュールに明示的なバージョンをタグ付けすることで、特定のバージョンが完全にテストされ、使用可能であることをユーザーに知らせます。
リポジトリにバージョンをタグ付けし始めたら、モジュールを開発する際に新しいリリースをタグ付けし続けることが重要です。ユーザーがモジュールの新しいバージョンを要求すると(go get -uまたはgo get example.com/helloで)、goコマンドは利用可能な最大のセマンティックリリースバージョンを選択します。たとえそのバージョンが数年前のもので、主要なブランチから多くの変更が遅れていてもです。新しいリリースをタグ付けし続けることで、継続的な改善がユーザーに提供されます。
リポジトリからバージョンタグを削除しないでください。バージョンにバグやセキュリティ上の問題が見つかった場合は、新しいバージョンをリリースしてください。もし削除したバージョンに依存している人がいると、ビルドが失敗する可能性があります。同様に、一度リリースしたバージョンを変更したり上書きしたりしないでください。モジュールミラーとチェックサムデータベースは、モジュール、そのバージョン、および署名された暗号ハッシュを保存し、与えられたバージョンのビルドが長期にわたって再現可能であることを保証します。
v0: 最初の不安定版
モジュールにv0セマンティックバージョンをタグ付けしましょう。v0バージョンは安定性を保証しないため、ほとんどすべてのプロジェクトは、公開APIを洗練させる際にv0から始めるべきです。
新しいバージョンをタグ付けするにはいくつかの手順があります。
-
go mod tidyを実行します。これにより、モジュールが蓄積したものの、もはや必要ない依存関係がすべて削除されます。 -
すべてが機能していることを確認するために、
go test ./...をもう一度実行します。 -
git tagを使用して、プロジェクトに新しいバージョンをタグ付けします。 -
新しいタグを元のリポジトリにプッシュします。
$ go mod tidy
$ go test ./...
ok example.com/hello 0.015s
$ git add go.mod go.sum hello.go hello_test.go
$ git commit -m "hello: changes for v0.1.0"
$ git tag v0.1.0
$ git push origin v0.1.0
$
これで、他のプロジェクトはexample.com/helloのv0.1.0に依存できます。自分のモジュールの場合、go list -m example.com/hello@v0.1.0を実行して最新バージョンが利用可能であることを確認できます(この例のモジュールは存在しないため、利用可能なバージョンはありません)。すぐに最新バージョンが表示されない場合で、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の一貫性を犠牲にして後方互換性を維持する典型的な例です。
しかし、Replaceは、文字列のインスタンスを先頭からいくつ置き換えるか(Splitとは異なり)のカウントを受け取りました。
SplitとSplitNがあれば、ReplaceやReplaceNのような関数を期待するでしょう。しかし、既存のReplaceを呼び出し元を壊さずに変更することはできませんでした。これは、私たちが行わないと約束したことです。そこで、Go 1.12では、新しい関数ReplaceAllを追加しました。その結果、SplitとReplaceの動作が異なるため、APIは少し奇妙になりましたが、この不一致は破壊的な変更よりも優れています。
example.com/helloのAPIに満足しており、最初の安定版としてv1をリリースしたいとします。
v1のタグ付けは、v0バージョンのタグ付けと同じプロセスを使用します: go mod tidyとgo test ./...を実行し、バージョンをタグ付けし、タグを元のリポジトリにプッシュします。
$ go mod tidy
$ go test ./...
ok example.com/hello 0.015s
$ git add go.mod go.sum hello.go hello_test.go
$ git commit -m "hello: changes for v1.0.0"
$ git tag v1.0.0
$ git push origin v1.0.0
$
この時点で、example.com/helloのv1 APIは確立されました。これは、私たちのAPIが安定しており、ユーザーが安心して使用できることをすべての人に伝えます。
まとめ
この記事では、セマンティックバージョンでモジュールをタグ付けするプロセスと、v1をリリースするタイミングについて説明しました。今後の記事では、v2以降のモジュールを維持および公開する方法について説明します。
Goにおける依存関係管理の将来についてフィードバックを提供し、その形成にご協力いただくため、バグレポートまたは体験レポートをお送りください。
Goモジュールの改善にご協力いただき、すべてのフィードバックに感謝いたします。
次の記事: Go 1.13でのエラーの扱い
前の記事: Go 1.13がリリースされました
ブログインデックス