Movatterモバイル変換


[0]ホーム

URL:


The Go Blog

Publishing Go Modules

Tyler Bui-Palsulich
26 September 2019

Introduction

This post is part 3 in a series.

Note: For documentation on developing modules, seeDeveloping and publishing modules.

This post discusses how to write and publish modules so other modules can dependon them.

Please note: this post covers development up to and includingv1. If you areinterested inv2, please seeGo Modules: v2 and Beyond.

This post usesGit in examples.Mercurial,Bazaar, and others are supported as well.

Project setup

For this post, you’ll need an existing project to use as an example. So, startwith the files from the end of theUsing Go Modules article:

$ cat go.modmodule example.com/hellogo 1.12require rsc.io/quote/v3 v3.1.0$ cat go.sumgolang.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.gopackage helloimport "rsc.io/quote/v3"func Hello() string {    return quote.HelloV3()}func Proverb() string {    return quote.Concurrency()}$ cat hello_test.gopackage helloimport (    "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)    }}$

Next, create a newgit repository and add an initial commit. If you’republishing your own project, be sure to include aLICENSE file. Change to thedirectory containing thego.mod then create the repo:

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

Semantic versions and modules

Every required module in ago.mod has asemantic version, the minimum version of that dependencyto use to build the module.

A semantic version has the formvMAJOR.MINOR.PATCH.

  • Increment theMAJOR version when you make abackwards incompatiblechange to the public API of your module.This should only be done when absolutely necessary.
  • Increment theMINOR version when you make a backwards compatible change to the API,like changing dependencies or adding a new function,method, struct field, or type.
  • Increment thePATCH version after making minor changes that don’t affectyour module’s public API or dependencies, like fixing a bug.

You can specify pre-release versions by appending a hyphen and dot separatedidentifiers (for example,v1.0.1-alpha orv2.2.2-beta.2). Normal releasesare preferred by thego command over pre-release versions, so users must askfor pre-release versions explicitly (for example,go get example.com/hello@v1.0.1-alpha) if your module has any normal releases.

v0 major versions and pre-release versions do not guarantee backwardscompatibility. They let you refine your API before making stability commitmentsto your users. However,v1 major versions and beyond require backwardscompatibility within that major version.

The version referenced in ago.mod may be an explicit release tagged in therepository (for example,v1.5.2), or it may be apseudo-version based on aspecific commit (for example,v0.0.0-20170915032832-14c0d48ead0c).Pseudo-versions are a special type of pre-release version. Pseudo-versions areuseful when a user needs to depend on a project that has not published anysemantic version tags, or develop against a commit that hasn’t been tagged yet,but users should not assume that pseudo-versions provide a stable or well-testedAPI. Tagging your modules with explicit versions signals to your users thatspecific versions are fully tested and ready to use.

Once you start tagging your repo with versions, it’s important to keep taggingnew releases as you develop your module. When users request a new version ofyour module (withgo get -u orgo get example.com/hello), thego commandwill choose the greatest semantic release version available, even if thatversion is several years old and many changes behind the primary branch.Continuing to tag new releases will make your ongoing improvements available toyour users.

Do not delete version tags from your repo. If you find a bug or a security issuewith a version, release a new version. If people depend on a version that youhave deleted, their builds may fail. Similarly, once you release a version, donot change or overwrite it. Themodule mirror and checksum databasestore modules, their versions, and signed cryptographic hashes to ensure thatthe build of a given version remains reproducible over time.

v0: the initial, unstable version

Let’s tag the module with av0 semantic version. Av0 version does not makeany stability guarantees, so nearly all projects should start withv0 as theyrefine their public API.

Tagging a new version has a few steps:

  1. Rungo mod tidy, which removes any dependencies the module might have accumulated that are no longer necessary.

  2. Rungo test ./... a final time to make sure everything is working.

  3. Tag the project with a new version usinggit tag.

  4. Push the new tag to the origin repository.

$ 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$

Now other projects can depend onv0.1.0 ofexample.com/hello. For your ownmodule, you can rungo list -m example.com/hello@v0.1.0 to confirm the latestversion is available (this example module does not exist, so no versions areavailable). If you don’t see the latest version immediately and you’re using theGo module proxy (the default since Go 1.13), try again in a few minutes to givethe proxy time to load the new version.

If you add to the public API, make a breaking change to av0 module, orupgrade the minor or version of one of your dependencies, increment theMINORversion for your next release. For example, the next release afterv0.1.0would bev0.2.0.

If you fix a bug in an existing version, increment thePATCH version. Forexample, the next release afterv0.1.0 would bev0.1.1.

v1: the first stable version

Once you are absolutely sure your module’s API is stable, you can releasev1.0.0. Av1 major version communicates to users that no incompatiblechanges will be made to the module’s API. They can upgrade to newv1 minor andpatch releases, and their code should not break. Function and method signatureswill not change, exported types will not be removed, and so on. If there arechanges to the API, they will be backwards compatible (for example, adding a newfield to a struct) and will be included in a new minor release. If there are bugfixes (for example, a security fix), they will be included in a patch release(or as part of a minor release).

Sometimes, maintaining backwards compatibility can lead to awkward APIs. That’sOK. An imperfect API is better than breaking users’ existing code.

The standard library’sstrings package is a prime example of maintainingbackwards compatibility at the cost of API consistency.

  • Split slices a string into allsubstrings separated by a separator and returns a slice of the substringsbetween those separators.
  • SplitN can be used to control the number of substrings to return.

However,Replace took a count of howmany instances of the string to replace from the beginning (unlikeSplit).

GivenSplit andSplitN, you would expect functions likeReplace andReplaceN. But, we couldn’t change the existingReplace without breakingcallers, which we promised not to do. So, in Go 1.12, we added a new function,ReplaceAll. The resulting API is alittle odd, sinceSplit andReplace behave differently, but thatinconsistency is better than a breaking change.

Let’s say you’re happy with the API ofexample.com/hello and you want toreleasev1 as the first stable version.

Taggingv1 uses the same process as tagging av0 version: rungo mod tidyandgo test ./..., tag the version, and push the tag to the origin repository:

$ 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$

At this point, thev1 API ofexample.com/hello is solidified. Thiscommunicates to everyone that our API is stable and they should feel comfortableusing it.

Conclusion

This post walked through the process of tagging a module with semantic versionsand when to releasev1. A future post will cover how to maintain and publishmodules atv2 and beyond.

To provide feedback and help shape the future of dependency management in Go,please send usbug reports orexperience reports.

Thanks for all your feedback and help improving Go modules.

Next article:Working with Errors in Go 1.13
Previous article:Go 1.13 is released
Blog Index

go.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.Learn more.

[8]ページ先頭

©2009-2025 Movatter.jp